import { useEffect, useMemo, useState } from "react"; type TaskId = "alert_triage" | "attack_chain_reconstruction" | "constrained_incident_response"; type Difficulty = "easy" | "medium" | "hard"; type TaskDefinition = { id: TaskId; name: string; difficulty: Difficulty; max_steps: number; description: string; }; type AlertRecord = { alert_id?: string; severity?: string; host_id?: string; user_id?: string | null; rule_name?: string; mitre_tactic?: string | null; status?: string; enrichment?: unknown; indicators?: Array<{ type?: string; value?: string; reputation?: string; }>; }; type HostRecord = { host_id?: string; hostname?: string; status?: string; role?: string; is_critical?: boolean; is_vip?: boolean; }; type ConstraintRecord = { host_id?: string | null; user_id?: string | null; constraint_type?: string; severity?: string; reason?: string; }; type NoteRecord = { step?: number; action_taken?: string; finding?: string; timestamp?: string; }; type Observation = { step: number; task_id: string; task_description: string; active_alerts: AlertRecord[]; acknowledged_alerts?: AlertRecord[]; hosts: HostRecord[]; business_constraints: ConstraintRecord[]; notes: NoteRecord[]; elapsed_minutes: number; steps_remaining: number; last_action_result?: string | null; last_action_success?: boolean; }; type EnvState = { task_id: string; step: number; done: boolean; cumulative_reward: number; observation: Observation; attack_chain?: Record | null; identified_stages?: string[]; isolated_hosts?: string[]; disabled_accounts?: string[]; forensics_collected?: Record; escalated?: boolean; ticket_created?: boolean; }; type StepReward = { total?: number; classification_delta?: number; chain_coverage_delta?: number; containment_delta?: number; dwell_penalty?: number; fp_penalty?: number; compliance_delta?: number; info?: string; }; type StepResponse = { observation?: Observation; reward?: StepReward; done?: boolean; info?: Record; }; type GradeResponse = { task_id?: string; score?: number; breakdown?: Record; }; type RunTrace = { id: string; kind: "reset" | "step" | "grade" | "error" | "info"; title: string; detail: string; reward?: number; done?: boolean; actionType?: string; createdAt: string; }; type TaskSeed = { action_type: string; alert_id?: string; alert_ids?: string[]; host_id?: string; user_id?: string; artifact_types?: string[]; source?: string; priority?: "P1" | "P2" | "P3"; summary?: string; correlation_hypothesis?: string; }; const FALLBACK_TASKS: TaskDefinition[] = [ { id: "alert_triage", name: "Alert triage", difficulty: "easy", max_steps: 10, description: "Classify noisy SIEM alerts, enrich the useful ones, and contain the true positives." }, { id: "attack_chain_reconstruction", name: "Attack chain reconstruction", difficulty: "medium", max_steps: 25, description: "Correlate alerts across hosts, identify the ATT&CK chain, and stop lateral movement." }, { id: "constrained_incident_response", name: "Constrained incident response", difficulty: "hard", max_steps: 40, description: "Respond to an active breach while respecting legal hold, customer-facing, and hard-block constraints." } ]; const API_ROOT = import.meta.env.VITE_API_BASE_URL ?? window.location.origin; const createSuggestedAction = (taskId: TaskId, observation: Observation | null): TaskSeed => { const activeAlerts = observation?.active_alerts ?? []; const hosts = observation?.hosts ?? []; const constraints = observation?.business_constraints ?? []; const firstAlertId = typeof activeAlerts[0]?.alert_id === "string" ? activeAlerts[0].alert_id : "ALT-001"; const secondAlertId = typeof activeAlerts[1]?.alert_id === "string" ? activeAlerts[1].alert_id : firstAlertId; const criticalHost = hosts.find((host) => host.is_critical && typeof host.host_id === "string")?.host_id; const legalHoldHost = constraints.find( (constraint) => constraint.constraint_type === "legal_hold" && typeof constraint.host_id === "string" )?.host_id; const preferredHost = criticalHost ?? legalHoldHost ?? hosts[0]?.host_id ?? "HOST-001"; if (taskId === "alert_triage") { return { action_type: "enrich_alert", alert_id: firstAlertId, source: "threat_intel" }; } if (taskId === "attack_chain_reconstruction") { return { action_type: "correlate_alerts", alert_ids: [firstAlertId, secondAlertId], correlation_hypothesis: "Linked intrusion activity across multiple hosts" }; } if (constraints.some((constraint) => constraint.constraint_type === "legal_hold")) { return { action_type: "collect_forensics", host_id: String(legalHoldHost ?? preferredHost), artifact_types: ["memory_dump", "event_logs"] }; } return { action_type: "create_ticket", priority: "P1", summary: "Critical incident requires immediate senior analyst review" }; }; const buildGuidedScript = (taskId: TaskId, observation: Observation | null): TaskSeed[] => { const activeAlerts = observation?.active_alerts ?? []; const hosts = observation?.hosts ?? []; const constraints = observation?.business_constraints ?? []; const firstAlertId = typeof activeAlerts[0]?.alert_id === "string" ? activeAlerts[0].alert_id : "ALT-001"; const secondAlertId = typeof activeAlerts[1]?.alert_id === "string" ? activeAlerts[1].alert_id : firstAlertId; const criticalHost = hosts.find((host) => host.is_critical && typeof host.host_id === "string")?.host_id; const legalHoldHost = constraints.find( (constraint) => constraint.constraint_type === "legal_hold" && typeof constraint.host_id === "string" )?.host_id; const customerFacingHost = constraints.find( (constraint) => constraint.constraint_type === "customer_facing" && typeof constraint.host_id === "string" )?.host_id; const hostTarget = criticalHost ?? legalHoldHost ?? customerFacingHost ?? hosts[0]?.host_id ?? "HOST-001"; if (taskId === "alert_triage") { return [ { action_type: "enrich_alert", alert_id: firstAlertId, source: "threat_intel" }, { action_type: "create_ticket", priority: "P2", summary: "Escalate the triage outcome and document containment evidence" } ]; } if (taskId === "attack_chain_reconstruction") { return [ { action_type: "enrich_alert", alert_id: firstAlertId, source: "asset_db" }, { action_type: "correlate_alerts", alert_ids: [firstAlertId, secondAlertId], correlation_hypothesis: "Single intrusion spanning multiple hosts and stages" }, { action_type: "isolate_endpoint", host_id: String(hostTarget) }, { action_type: "create_ticket", priority: "P1", summary: "Attack chain identified and containment actions executed" } ]; } return [ { action_type: "collect_forensics", host_id: String(legalHoldHost ?? hostTarget), artifact_types: ["memory_dump", "event_logs", "network_capture"] }, { action_type: "escalate_to_tier2", summary: "Active breach with legal and business constraints requires senior review" }, { action_type: "create_ticket", priority: "P1", summary: "Open incident record and preserve evidence chain" } ]; }; const formatJson = (value: unknown) => JSON.stringify(value, null, 2); function HomePage() { const [tasks, setTasks] = useState(FALLBACK_TASKS); const [selectedTaskId, setSelectedTaskId] = useState("alert_triage"); const [busy, setBusy] = useState(null); const [statusText, setStatusText] = useState("Ready to reset an episode."); const [draftAction, setDraftAction] = useState(formatJson(createSuggestedAction("alert_triage", null))); const [observation, setObservation] = useState(null); const [stateData, setStateData] = useState(null); const [gradeData, setGradeData] = useState(null); const [trace, setTrace] = useState([]); const [backendError, setBackendError] = useState(""); const selectedTask = tasks.find((task) => task.id === selectedTaskId) ?? tasks[0]; const activeAlerts = observation?.active_alerts ?? []; const acknowledgedAlerts = observation?.acknowledged_alerts ?? []; const hosts = observation?.hosts ?? []; const constraints = observation?.business_constraints ?? []; const notes = observation?.notes ?? []; const counts = useMemo(() => { return { alerts: activeAlerts.length, acknowledged: acknowledgedAlerts.length, hosts: hosts.length, constraints: constraints.length, notes: notes.length }; }, [activeAlerts.length, acknowledgedAlerts.length, hosts.length, constraints.length, notes.length]); useEffect(() => { const loadTasks = async () => { try { const response = await fetch(`${API_ROOT}/api/tasks`); if (!response.ok) { throw new Error(`Task list request failed with status ${response.status}`); } const payload = (await response.json()) as { tasks?: TaskDefinition[] }; if (Array.isArray(payload.tasks) && payload.tasks.length > 0) { setTasks(payload.tasks); if (!payload.tasks.some((task) => task.id === selectedTaskId) && payload.tasks[0]) { setSelectedTaskId(payload.tasks[0].id); } } setBackendError(""); } catch (error) { const message = error instanceof Error ? error.message : "Failed to load tasks"; setBackendError(message); } }; void loadTasks(); }, []); useEffect(() => { setDraftAction(formatJson(createSuggestedAction(selectedTaskId, observation))); }, [selectedTaskId]); useEffect(() => { if (!tasks.some((task) => task.id === selectedTaskId) && tasks[0]) { setSelectedTaskId(tasks[0].id); } }, [tasks, selectedTaskId]); const pushTrace = ( entry: Omit ) => { setTrace((current) => [ { ...entry, id: `${Date.now()}-${Math.random().toString(16).slice(2)}`, createdAt: new Date().toISOString() }, ...current ].slice(0, 32)); }; const syncState = async (taskId: TaskId) => { const response = await fetch(`${API_ROOT}/state?task_id=${taskId}`); if (!response.ok) { throw new Error(`State request failed with status ${response.status}`); } const payload = (await response.json()) as EnvState; setStateData(payload); return payload; }; const resetEpisode = async (taskId: TaskId = selectedTaskId) => { setBusy("reset"); setStatusText(`Resetting ${taskId}...`); try { const response = await fetch(`${API_ROOT}/reset`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ task_id: taskId, seed: 42 }) }); if (!response.ok) { const details = await response.text(); throw new Error(`Reset failed (${response.status}): ${details}`); } const payload = (await response.json()) as Observation; setObservation(payload); const state = await syncState(taskId); setGradeData(null); setBackendError(""); setDraftAction(formatJson(createSuggestedAction(taskId, payload))); setStatusText(`Episode reset for ${taskId}. Ready for the next action.`); pushTrace({ kind: "reset", title: "Episode reset", detail: `Loaded ${taskId} with ${payload.active_alerts.length} active alerts and ${payload.hosts.length} hosts.` }); return { observation: payload, state }; } catch (error) { const message = error instanceof Error ? error.message : "Unknown reset error"; setBackendError(message); setStatusText("Reset failed."); pushTrace({ kind: "error", title: "Reset error", detail: message }); throw error; } finally { setBusy(null); } }; const parseDraft = () => { const parsed = JSON.parse(draftAction) as TaskSeed; if (!parsed || typeof parsed !== "object" || typeof parsed.action_type !== "string") { throw new Error("Action JSON must include action_type."); } return parsed; }; const sendStep = async (action: TaskSeed, label: string) => { setBusy("step"); setStatusText(`${label}...`); try { const response = await fetch(`${API_ROOT}/step`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ task_id: selectedTaskId, action }) }); if (!response.ok) { const details = await response.text(); throw new Error(`Step failed (${response.status}): ${details}`); } const payload = (await response.json()) as StepResponse; const nextObservation = payload.observation ?? null; setObservation(nextObservation); const state = await syncState(selectedTaskId); setBackendError(""); const reward = payload.reward?.total ?? 0; const done = Boolean(payload.done); const actionType = action.action_type; setStatusText(`${label} complete. Reward ${reward.toFixed(3)}. Done: ${done ? "yes" : "no"}.`); pushTrace({ kind: "step", title: label, detail: payload.reward?.info ?? `Action ${actionType} executed.`, reward, done, actionType }); return { payload, state }; } catch (error) { const message = error instanceof Error ? error.message : "Unknown step error"; setBackendError(message); setStatusText("Action failed."); pushTrace({ kind: "error", title: "Step error", detail: message }); throw error; } finally { setBusy(null); } }; const gradeEpisode = async () => { setBusy("grade"); setStatusText(`Grading ${selectedTaskId}...`); try { const response = await fetch(`${API_ROOT}/grade?task_id=${selectedTaskId}`, { method: "POST" }); if (!response.ok) { const details = await response.text(); throw new Error(`Grade failed (${response.status}): ${details}`); } const payload = (await response.json()) as GradeResponse; setGradeData(payload); setBackendError(""); const score = typeof payload.score === "number" ? payload.score : 0; setStatusText(`Grade complete. Score ${score.toFixed(3)}.`); pushTrace({ kind: "grade", title: "Episode graded", detail: `Task ${selectedTaskId} scored ${score.toFixed(3)}.`, reward: score }); return payload; } catch (error) { const message = error instanceof Error ? error.message : "Unknown grade error"; setBackendError(message); setStatusText("Grade failed."); pushTrace({ kind: "error", title: "Grade error", detail: message }); throw error; } finally { setBusy(null); } }; const runGuidedDemo = async () => { setBusy("demo"); setBackendError(""); try { const resetResult = await resetEpisode(selectedTaskId); const script = buildGuidedScript(selectedTaskId, resetResult.observation); for (const [index, action] of script.entries()) { const response = await sendStep(action, `Guided step ${index + 1}`); if (response.payload.done) { break; } } await gradeEpisode(); setStatusText(`Guided demo completed for ${selectedTaskId}.`); } catch { // errors already surfaced in trace and status } finally { setBusy(null); } }; const loadSuggestedAction = () => { setDraftAction(formatJson(createSuggestedAction(selectedTaskId, observation))); }; const submitDraftAction = async () => { const action = parseDraft(); await sendStep(action, `Manual step for ${selectedTaskId}`); }; return (

OpenEnv hackathon demo

Security Operations Center simulation with live reset, step, state, and grade flows.

The environment is the simulator. The model is only the policy that emits JSON actions. This console shows both sides clearly so judges can inspect the episode, the response, the reward, and the final score without reading code.

Tasks {tasks.length} easy → hard
Backend FastAPI /reset /step /state /grade
Baseline HF router OpenAI client compatible

Active episode

{selectedTask?.name ?? selectedTaskId}

Reward {stateData?.cumulative_reward?.toFixed(3) ?? "0.000"} Step {stateData?.step ?? observation?.step ?? 0} Done {stateData?.done ? "yes" : "no"}

{selectedTask?.description ?? "Select a task to inspect the current incident."}

Active alerts {counts.alerts}
Acknowledged {counts.acknowledged}
Hosts {counts.hosts}
Constraints {counts.constraints}

Current observation

Visible to the agent
{formatJson(observation ?? { note: "Reset an episode to load data." })}

Backend state

Includes hidden grading state
{formatJson(stateData ?? { note: "State appears after the first reset." })}

Action draft

JSON action submitted to /step

HF client Deterministic grader