Observe: per-agent drill-in from the World view
ci / gates (push) Has been cancelled
ci / sandbox-k8s (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / rust (push) Has been cancelled
ci / e2e (push) Has been cancelled

Clicking a neuron/pawn now focuses the Observe panel on that agent: a header with
its role + live status + a "← System" back button, then that agent's filtered
REASONING STREAM and TOOL CALLS. Feed items are tagged by agentId; the system
view shows the agent id per line. Wired via worldSel in Dashboard.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-24 04:24:42 -07:00
co-authored by Claude Opus 4.8
parent c5daf5925c
commit b9d3fc4f44
2 changed files with 68 additions and 6 deletions
@@ -590,7 +590,7 @@ export function Dashboard({ user, orgs, claws }: { user?: { display_name?: strin
</div> </div>
{(() => { {(() => {
const loc = locateAgent(worldSel); const loc = locateAgent(worldSel);
if (!loc) return <ObservePanel />; if (!loc) return <ObservePanel focusAgent={worldSel} onClearFocus={() => setWorldSel(null)} />;
const { org: o, company: co, team: t, agent: a } = loc; const { org: o, company: co, team: t, agent: a } = loc;
const statusCol = a.status === "running" ? "#5ec8d8" : a.status === "online" ? "#5fd08a" : "#5a5a62"; const statusCol = a.status === "running" ? "#5ec8d8" : a.status === "online" ? "#5fd08a" : "#5a5a62";
const chip = (text: string, col = "#9a9aa2") => <span style={{ fontFamily: mono, fontSize: 10, color: col, padding: "3px 9px", borderRadius: 999, background: `${col}1a`, border: `1px solid ${col}33` }}>{text}</span>; const chip = (text: string, col = "#9a9aa2") => <span style={{ fontFamily: mono, fontSize: 10, color: col, padding: "3px 9px", borderRadius: 999, background: `${col}1a`, border: `1px solid ${col}33` }}>{text}</span>;
@@ -16,6 +16,7 @@ interface FeedItem {
kind: "tool" | "think" | "door"; kind: "tool" | "think" | "door";
text: string; text: string;
color: string; color: string;
agentId: string;
} }
interface Door { interface Door {
doorId: string; doorId: string;
@@ -35,7 +36,10 @@ function Section({ label, children }: { label: string; children: React.ReactNode
); );
} }
export function ObservePanel() { export function ObservePanel({
focusAgent,
onClearFocus,
}: { focusAgent?: string | null; onClearFocus?: () => void } = {}) {
const telemetry = useLiveState<"telemetry", { doorsPending?: number; loops?: number; costPerHr?: number; tokensPerMin?: number }>( const telemetry = useLiveState<"telemetry", { doorsPending?: number; loops?: number; costPerHr?: number; tokensPerMin?: number }>(
"telemetry", "telemetry",
(d) => d, (d) => d,
@@ -56,15 +60,15 @@ export function ObservePanel() {
const buf = useRef<FeedItem[]>([]); const buf = useRef<FeedItem[]>([]);
const seq = useRef(0); const seq = useRef(0);
const [feed, setFeed] = useState<FeedItem[]>([]); const [feed, setFeed] = useState<FeedItem[]>([]);
const push = (kind: FeedItem["kind"], text: string, color: string) => { const push = (kind: FeedItem["kind"], text: string, color: string, agentId: string) => {
buf.current = [{ id: seq.current++, kind, text, color }, ...buf.current].slice(0, 60); buf.current = [{ id: seq.current++, kind, text, color, agentId }, ...buf.current].slice(0, 60);
}; };
useLiveEvent("agent.tool.call", (d) => useLiveEvent("agent.tool.call", (d) =>
push("tool", `${short(d.agentId)} → ${d.tool}${d.target ? ` ${d.target}` : ""}`, "#5ec8d8"), push("tool", `→ ${d.tool}${d.target ? ` ${d.target}` : ""}`, "#5ec8d8", d.agentId),
); );
useLiveEvent("agent.reasoning.delta", (d) => { useLiveEvent("agent.reasoning.delta", (d) => {
const t = d.text.trim(); const t = d.text.trim();
if (t) push("think", `${short(d.agentId)} · ${t}`, "#9a9aa2"); if (t) push("think", t, "#9a9aa2", d.agentId);
}); });
useEffect(() => { useEffect(() => {
const id = setInterval(() => setFeed(buf.current.slice()), 300); const id = setInterval(() => setFeed(buf.current.slice()), 300);
@@ -79,6 +83,63 @@ export function ObservePanel() {
</div> </div>
); );
// Per-agent drill-in: when a neuron/pawn is selected and we have live data for
// it, focus the panel on that agent's reasoning + tools.
const focused = focusAgent && (agents[focusAgent] || feed.some((f) => f.agentId === focusAgent)) ? focusAgent : null;
if (focused) {
const a = agents[focused];
const items = feed.filter((f) => f.agentId === focused);
const think = items.filter((f) => f.kind === "think");
const tools = items.filter((f) => f.kind === "tool");
return (
<div style={{ flex: 1, minHeight: 0, overflow: "auto", padding: 16, display: "flex", flexDirection: "column", gap: 16 }}>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<button
type="button"
onClick={onClearFocus}
style={{ fontFamily: mono, fontSize: 10.5, padding: "4px 9px", borderRadius: 7, border: "1px solid rgba(255,255,255,.12)", background: "rgba(255,255,255,.04)", color: "#cfcfd5", cursor: "pointer" }}
>
← System
</button>
<span style={{ flex: 1 }} />
<span style={{ width: 8, height: 8, borderRadius: "50%", background: statusColor(a?.status ?? "idle") }} />
<span style={{ fontFamily: mono, fontSize: 10, color: statusColor(a?.status ?? "idle") }}>{a?.status ?? "—"}</span>
</div>
<div>
<div style={{ fontSize: 15, fontWeight: 700, color: "#f3f3f5" }}>{a?.role || short(focused)}</div>
<div style={{ fontFamily: mono, fontSize: 10, color: "#6a6a72" }}>observing · {short(focused)}</div>
</div>
<Section label="REASONING STREAM">
{think.length === 0 ? (
<span style={{ fontFamily: mono, fontSize: 11, color: "#3a3a40" }}>no reasoning yet…</span>
) : (
<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
{think.map((f) => (
<div key={f.id} style={{ fontFamily: mono, fontSize: 11, color: "#cfcfd5", lineHeight: 1.5 }}>
{f.text}
</div>
))}
</div>
)}
</Section>
<Section label="TOOL CALLS">
{tools.length === 0 ? (
<span style={{ fontFamily: mono, fontSize: 11, color: "#3a3a40" }}>no tool calls yet…</span>
) : (
<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
{tools.map((f) => (
<div key={f.id} style={{ display: "flex", gap: 8, fontFamily: mono, fontSize: 10.5 }}>
<span style={{ color: "#5ec8d8" }}>⚙</span>
<span style={{ color: "#cfcfd5" }}>{f.text}</span>
</div>
))}
</div>
)}
</Section>
</div>
);
}
const agentList = Object.entries(agents); const agentList = Object.entries(agents);
return ( return (
@@ -125,6 +186,7 @@ export function ObservePanel() {
{feed.map((f) => ( {feed.map((f) => (
<div key={f.id} style={{ display: "flex", gap: 8, fontFamily: mono, fontSize: 10.5, lineHeight: 1.5 }}> <div key={f.id} style={{ display: "flex", gap: 8, fontFamily: mono, fontSize: 10.5, lineHeight: 1.5 }}>
<span style={{ flex: "none", color: f.color }}>{f.kind === "tool" ? "⚙" : "›"}</span> <span style={{ flex: "none", color: f.color }}>{f.kind === "tool" ? "⚙" : "›"}</span>
<span style={{ flex: "none", color: "#6a6a72" }}>{short(f.agentId)}</span>
<span style={{ flex: 1, minWidth: 0, color: "#cfcfd5", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{f.text}</span> <span style={{ flex: 1, minWidth: 0, color: "#cfcfd5", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{f.text}</span>
</div> </div>
))} ))}