// Task detail drawer — right-side panel that streams the agent's live conversation function TaskDrawer({ taskId, onClose }) { if (!taskId) return null; const stream = window.Gastronaut?.useTaskStream?.(taskId); const task = stream?.task; const messages = stream?.messages || []; const status = task?.status || "running"; const agentColor = task?.agentColor === "gold" ? "var(--gold)" : "var(--accent)"; const bodyRef = React.useRef(null); React.useEffect(() => { if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight; }, [messages.length]); React.useEffect(() => { const onKey = (e) => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [onClose]); const statusPill = { running: { bg: "rgba(6,182,212,0.15)", c: "var(--accent)", label: "running" }, queued: { bg: "rgba(255,255,255,0.06)", c: "var(--ink-2)", label: "queued" }, done: { bg: "rgba(34,197,94,0.15)", c: "var(--good)", label: "done" }, error: { bg: "rgba(239,68,68,0.15)", c: "var(--danger)", label: "error" }, }[status] || { bg: "rgba(255,255,255,0.06)", c: "var(--ink-2)", label: status }; return (
e.stopPropagation()} style={{ width: "min(620px, 100%)", height: "100%", borderRadius: 0, borderLeft: "1px solid var(--line-2)", display: "flex", flexDirection: "column", animation: "slideIn 220ms cubic-bezier(0.2,0.8,0.2,1)", }} > {/* Header */}
{task?.agentName || "Agent"} · {statusPill.label}
{task?.prompt || "…"}
{taskId} {task?.createdAt ? "· " + new Date(task.createdAt).toLocaleTimeString() : ""}
{/* Messages */}
{messages.length === 0 && (
waiting for agent…
)} {messages.map((m, i) => )} {status === "running" && (
agent is thinking…
)}
{/* Footer */}
{messages.length} message{messages.length === 1 ? "" : "s"} {task?.finishedAt ? " · " + Math.round((new Date(task.finishedAt) - new Date(task.createdAt))/1000) + "s" : ""} esc to close
); } function DrawerMessage({ msg, color }) { if (msg.role === "user") { return (
{msg.text}
); } if (msg.role === "assistant") { return (
assistant
{msg.text}
); } if (msg.role === "tool_use") { const input = msg.input ? (typeof msg.input === "string" ? msg.input : JSON.stringify(msg.input, null, 2)) : ""; return (
→ tool · {msg.tool}
{input &&
{input.slice(0, 1200)}
}
); } if (msg.role === "tool_result") { return (
← {msg.isError ? "tool error" : "tool result"}
{msg.text &&
{msg.text}
}
); } if (msg.role === "error") { return (
{msg.text}
); } return null; } // New Task modal — chat-style composer with agent assignment function NewTaskModal({ open, onClose, onSubmitted }) { const [text, setText] = React.useState(""); const [agentId, setAgentId] = React.useState("auto"); const [priority, setPriority] = React.useState("normal"); React.useEffect(() => { if (!open) return; const onKey = (e) => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [open, onClose]); if (!open) return null; const agents = [ { id: "auto", name: "Auto-route", role: "Prime decides", color: "cyan" }, { id: "prime", name: "Prime", role: "Orchestrator", color: "cyan" }, { id: "market", name: "Market Sentry", role: "Markets & risk", color: "gold" }, { id: "creative", name: "Creative Director", role: "Brand & copy", color: "cyan" }, { id: "research", name: "Research Specialist", role: "Primary research", color: "cyan" }, { id: "ops", name: "Ops Steward", role: "Infra & secrets", color: "cyan" }, { id: "scribe", name: "Scribe", role: "Git & docs", color: "cyan" }, { id: "tape", name: "Tape Reader", role: "Tick ingest", color: "gold" }, ]; const chosen = agents.find(a => a.id === agentId) || agents[0]; const chosenColor = chosen.color === "gold" ? "var(--gold)" : "var(--accent)"; const [sending, setSending] = React.useState(false); const submit = async () => { if (!text.trim() || sending) return; setSending(true); let taskId = null; try { if (window.Gastronaut?.submitTask) { const r = await window.Gastronaut.submitTask({ prompt: text, agentId, priority }); taskId = r?.id || null; } } catch (e) { console.error("[new-task] submit failed", e); } finally { setSending(false); onClose(); setText(""); setAgentId("auto"); if (taskId && onSubmitted) onSubmitted(taskId); } }; return (
e.stopPropagation()} style={{ width: "min(720px, 100%)", padding: 0, borderRadius: 22, overflow: "hidden", animation: "riseIn 240ms cubic-bezier(0.2, 0.8, 0.2, 1)", }} > {/* Header */}
New task

What should the team do?

{/* Chat-style composer */}