import { useState } from 'react' import { BrainCircuit, BookOpen, CornerDownLeft, Info, Loader2, Sparkles } from 'lucide-react' import { missionApi } from './api' import { missionBrand } from './brand' import { ApiError } from '../../lib/api' import { Badge, Panel } from '../../components/ui' import { ToolHeader } from '../../components/ToolHeader' import type { AskAnswer } from '../../lib/types' // Example prompts map to real registered intents and the seeded demo data, so the first click always // returns a grounded answer. Clicking a chip fills the input; the user still presses Ask to send it. const EXAMPLES = [ 'How many candidates hold a TS/SCI clearance?', 'What are our hardest staffing gaps right now?', 'Who can support the Intelligence Analyst mission?', 'What is the win probability on the Air Force segment?', 'Show the clearance coverage of our bench.', 'Which vendors perform best on our submissions?', ] // pathLabel names each retrieval route in plain words for the answer badge. const PATH_LABEL: Record = { cag: 'Closed answer', tag: 'Tool answer', rag: 'Retrieval', graph: 'Graph walk', } // The Ask panel is the UI over the closed Registry AI brain. The brain answers only from the tenant's // own cleared data, cites its evidence, and refuses questions about protected attributes or out of scope // questions. A refusal is a normal outcome the panel renders calmly, never an error. export function Ask() { const [question, setQuestion] = useState('') const [loading, setLoading] = useState(false) const [answer, setAnswer] = useState(null) // A refusal is the brain's calm decline (HTTP 422). errorLine is a generic friendly line for any other // failure. The two are kept separate so a refusal never reads as a crash. const [refusal, setRefusal] = useState(null) const [errorLine, setErrorLine] = useState(null) const trimmed = question.trim() const canAsk = trimmed.length > 0 && !loading async function submit() { if (!canAsk) return setLoading(true) setAnswer(null) setRefusal(null) setErrorLine(null) try { const res = await missionApi.ask(trimmed) setAnswer(res) } catch (err) { // A refused question returns 422 with an error body. Treat it as an expected, calm decline. if (err instanceof ApiError && err.status === 422) { setRefusal(err.message || 'That question is out of scope for the mission brain.') } else { setErrorLine('Something went wrong reaching the mission brain. Please try again.') } } finally { setLoading(false) } } function onKeyDown(e: React.KeyboardEvent) { // Enter with a modifier sends, so a plain Enter can still add a newline in a longer question. if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) { e.preventDefault() void submit() } } return (

Ask the Brain™

This is the grounded mission brain. It answers from your tenant's own cleared data and cites the evidence behind every answer.