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
@@ -16,6 +16,7 @@ interface FeedItem {
kind: "tool" | "think" | "door";
text: string;
color: string;
agentId: string;
}
interface Door {
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 }>(
"telemetry",
(d) => d,
@@ -56,15 +60,15 @@ export function ObservePanel() {
const buf = useRef<FeedItem[]>([]);
const seq = useRef(0);
const [feed, setFeed] = useState<FeedItem[]>([]);
const push = (kind: FeedItem["kind"], text: string, color: string) => {
buf.current = [{ id: seq.current++, kind, text, color }, ...buf.current].slice(0, 60);
const push = (kind: FeedItem["kind"], text: string, color: string, agentId: string) => {
buf.current = [{ id: seq.current++, kind, text, color, agentId }, ...buf.current].slice(0, 60);
};
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) => {
const t = d.text.trim();
if (t) push("think", `${short(d.agentId)} · ${t}`, "#9a9aa2");
if (t) push("think", t, "#9a9aa2", d.agentId);
});
useEffect(() => {
const id = setInterval(() => setFeed(buf.current.slice()), 300);
@@ -79,6 +83,63 @@ export function ObservePanel() {
</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);
return (
@@ -125,6 +186,7 @@ export function ObservePanel() {
{feed.map((f) => (
<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: "#6a6a72" }}>{short(f.agentId)}</span>
<span style={{ flex: 1, minWidth: 0, color: "#cfcfd5", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{f.text}</span>
</div>
))}