import React, { useState, useEffect, useRef } from "react"; import { motion, AnimatePresence } from "framer-motion"; const AgentBondWatermark = ({ size = 200, className = "" }) => ( {/* Subtle violet glow */} {/* Outer soft ring */} {/* Rim — glass edge */} {/* Lens fill — frosted glass */} {/* Highlight flare */} {/* Center node */} {/* Satellite nodes */} {/* Neural edges */} {/* Handle */} ); // Helper to highlight important keywords in verdict conclusions const highlightImportantText = (text) => { if (!text) return ""; const keywords = [ { pattern: /\b(not supported|contradicting|contradicts|refuted|inconclusive|disproved|no evidence)\b/gi, className: "text-red-400 font-medium" }, { pattern: /\b(supported|largely supported|verified|confirmed|authentic)\b/gi, className: "text-emerald-400 font-medium" }, { pattern: /\b(unsolved|no arrests|unconfirmed)\b/gi, className: "text-amber-400 font-medium" }, { pattern: /\b(FBI|official FBI|court documents|press releases|official reports|web search results|official documents|intelligence)\b/gi, className: "text-[#9E8EFD] font-medium" } ]; let parts = [{ text, isMatch: false }]; keywords.forEach(({ pattern, className }) => { const newParts = []; parts.forEach((part) => { if (part.isMatch) { newParts.push(part); } else { let lastIndex = 0; let match; pattern.lastIndex = 0; while ((match = pattern.exec(part.text)) !== null) { const index = match.index; const matchedText = match[0]; if (index > lastIndex) { newParts.push({ text: part.text.substring(lastIndex, index), isMatch: false }); } newParts.push({ text: matchedText, isMatch: true, className }); lastIndex = index + matchedText.length; } if (lastIndex < part.text.length) { newParts.push({ text: part.text.substring(lastIndex), isMatch: false }); } } }); parts = newParts; }); return parts.map((part, index) => { if (part.isMatch) { return {part.text}; } return part.text; }); }; const parseVerdict = (content) => { const cleanContent = content.replace("Hypothesis Verdict:", "").trim(); const match = cleanContent.match(/^'(.*?)'\s*->\s*(.*)$/); if (match) { return { hypothesis: match[1], conclusion: match[2] }; } const parts = cleanContent.split(" -> "); if (parts.length > 1) { return { hypothesis: parts[0].replace(/^'|'$/g, ''), conclusion: parts.slice(1).join(" -> ") }; } return { hypothesis: null, conclusion: cleanContent }; }; export default function WorkspaceSection({ onBackToLanding }) { const [cases, setCases] = useState([]); const [selectedCaseId, setSelectedCaseId] = useState(null); const [activeCase, setActiveCase] = useState(null); const [loadingCases, setLoadingCases] = useState(true); // Hidden cases state for local deletion/hiding const [hiddenCaseIds, setHiddenCaseIds] = useState(() => { try { const stored = localStorage.getItem("hidden_case_ids"); return stored ? JSON.parse(stored) : []; } catch (e) { return []; } }); const handleHideCase = (caseId) => { const updated = [...hiddenCaseIds, caseId]; setHiddenCaseIds(updated); localStorage.setItem("hidden_case_ids", JSON.stringify(updated)); if (selectedCaseId === caseId) { setSelectedCaseId(null); setIsCreatingNew(true); } }; const visibleCases = cases.filter((c) => !hiddenCaseIds.includes(c.case_id)); // Case object selected for custom ChatGPT-style deletion modal const [caseToDelete, setCaseToDelete] = useState(null); // Accordion state for hypotheses const [expandedHypothesisId, setExpandedHypothesisId] = useState(null); // New Case Form States const [isCreatingNew, setIsCreatingNew] = useState(true); const [problemStatement, setProblemStatement] = useState(""); const [constraints, setConstraints] = useState([""]); const [isSubmitting, setIsSubmitting] = useState(false); // Reference for keeping track of polling const pollingIntervalRef = useRef(null); // API base URL const API_BASE_URL = import.meta.env.VITE_API_URL || "http://localhost:8000"; // Helper for auth headers const getHeaders = () => { const token = localStorage.getItem("token"); return { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}), }; }; // Fetch past cases const fetchCases = async () => { try { const res = await fetch(`${API_BASE_URL}/api/cases`, { headers: getHeaders(), }); if (res.ok) { const data = await res.json(); setCases(data); if (data.length > 0 && !selectedCaseId && !isCreatingNew) { setSelectedCaseId(data[0].case_id); setIsCreatingNew(false); } } } catch (err) { console.error("Error fetching cases:", err); } finally { setLoadingCases(false); } }; // Fetch details for the selected case const fetchCaseDetails = async (caseId) => { try { const res = await fetch(`${API_BASE_URL}/api/cases/${caseId}`, { headers: getHeaders(), }); if (res.ok) { const data = await res.json(); setActiveCase(data); // Auto-expand first hypothesis if none is expanded and hypotheses are loaded if (data.hypotheses && data.hypotheses.length > 0 && !expandedHypothesisId) { setExpandedHypothesisId(data.hypotheses[0].id); } // Update in cases list to sync status setCases((prev) => prev.map((c) => (c.case_id === caseId ? { ...c, status: data.status } : c)) ); // Stop polling if completed or failed if (data.status === "completed" || data.status === "failed") { stopPolling(); } } else { stopPolling(); } } catch (err) { console.error("Error fetching case details:", err); stopPolling(); } }; // Start polling active case details const startPolling = (caseId) => { stopPolling(); fetchCaseDetails(caseId); pollingIntervalRef.current = setInterval(() => { fetchCaseDetails(caseId); }, 1500); }; // Stop polling const stopPolling = () => { if (pollingIntervalRef.current) { clearInterval(pollingIntervalRef.current); pollingIntervalRef.current = null; } }; useEffect(() => { fetchCases(); return () => stopPolling(); }, []); // Sync polling when selectedCaseId changes useEffect(() => { if (selectedCaseId) { setIsCreatingNew(false); setExpandedHypothesisId(null); // Reset accordion startPolling(selectedCaseId); } else { setActiveCase(null); stopPolling(); } return () => stopPolling(); }, [selectedCaseId]); // Handle selection of a case const handleSelectCase = (caseId) => { setSelectedCaseId(caseId); }; // Add constraint input const addConstraint = () => { setConstraints([...constraints, ""]); }; // Remove constraint input const removeConstraint = (index) => { setConstraints(constraints.filter((_, i) => i !== index)); }; // Update constraint text const updateConstraint = (index, value) => { const next = [...constraints]; next[index] = value; setConstraints(next); }; // Submit case to backend const handleSubmitCase = async (e) => { e.preventDefault(); if (!problemStatement.trim()) return; setIsSubmitting(true); try { const cleanConstraints = constraints.filter((c) => c.trim() !== ""); const createRes = await fetch(`${API_BASE_URL}/api/cases`, { method: "POST", headers: getHeaders(), body: JSON.stringify({ problem_statement: problemStatement, constraints: cleanConstraints, }), }); if (!createRes.ok) throw new Error("Failed to initialize case"); const newCase = await createRes.json(); const decompRes = await fetch(`${API_BASE_URL}/api/cases/${newCase.case_id}/decompose`, { method: "POST", headers: getHeaders(), }); if (!decompRes.ok) throw new Error("Failed to deploy agent pipeline"); const activeContext = await decompRes.json(); setProblemStatement(""); setConstraints([""]); await fetchCases(); setSelectedCaseId(activeContext.case_id); } catch (err) { console.error(err); alert(err.message || "An error occurred while deploying the agent pipeline."); } finally { setIsSubmitting(false); } }; return (
{/* ── SIDEBAR: PAST CASES ── */}

Investigation nodes

{/* Scrollable list */}
{loadingCases ? (
Accessing Context Store...
) : visibleCases.length === 0 ? (
📁 No investigation sessions stored yet.
) : ( visibleCases.map((c) => (
handleSelectCase(c.case_id)} className={`w-full text-left p-4 rounded-xl border transition-all duration-200 cursor-pointer flex flex-col justify-between gap-3 relative group ${ selectedCaseId === c.case_id ? "bg-[#7C5CFC]/10 border-[#7C5CFC]/40 shadow-lg shadow-[#7C5CFC]/5" : "bg-white/[0.02] border-white/5 hover:bg-white/[0.04] hover:border-white/10" }`} > {/* Delete button (client-side hide) */} {c.problem_statement}
{c.updated_at ? new Date(c.updated_at.endsWith("Z") ? c.updated_at : c.updated_at + "Z").toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : "Just now"} {c.status}
)) )}
{/* Back to Home CTA */}
{/* Sidebar Watermark */}
{/* ── MAIN WORKSPACE ── */}
{isCreatingNew ? ( /* CREATE CASE WORKSPACE */

Deploy Autonomous Pipeline

Enter an open-ended scenario or problem statement. Our Case Manager will decompose it into structured hypotheses, which are verified against DuckDuckGo searches by autonomous investigators.