World: Phase D — Observe live mission-control panel
ci / gates (push) Has been cancelled
ci / rust (push) Has been cancelled
ci / sandbox-k8s (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / e2e (push) Has been cancelled

frontend/src/components/observe/ObservePanel.tsx — a live mission-control view
driven entirely by the taxonomy feed (telemetry strip, doors awaiting approval,
agent statuses, and a rolling activity stream of real reasoning + tool calls;
the feed is ref-buffered + flushed on an interval so reasoning bursts don't
re-render per token). Rendered as the World slide-out's system view (replaces
the empty-state placeholder). With the run_events normalization, this shows
REAL agent reasoning, tool calls, and doors.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-23 21:56:51 -07:00
co-authored by Claude Opus 4.8
parent 380b95da2a
commit b1d18504c3
2 changed files with 140 additions and 5 deletions
@@ -0,0 +1,138 @@
"use client";
// Observe — live mission-control, driven entirely by the Clawmates taxonomy feed
// (so it's independent of the world graph's seed data). Shows the telemetry strip,
// doors awaiting approval, agent statuses, and a rolling activity stream of real
// reasoning + tool calls. Rendered in the World slide-out as its system view.
import { useEffect, useRef, useState } from "react";
import { useLiveEvent, useLiveState } from "@/lib/live/useClawmatesLive";
const mono = "'Geist Mono', ui-monospace, monospace";
interface FeedItem {
id: number;
kind: "tool" | "think" | "door";
text: string;
color: string;
}
interface Door {
doorId: string;
agentId: string;
action: string;
summary?: string;
}
const short = (id: string) => (id.length > 8 ? id.slice(0, 6) : id);
function Section({ label, children }: { label: string; children: React.ReactNode }) {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 7 }}>
<span style={{ fontFamily: mono, fontSize: 9, letterSpacing: ".12em", color: "#5a5a62" }}>{label}</span>
{children}
</div>
);
}
export function ObservePanel() {
const telemetry = useLiveState<"telemetry", { doorsPending?: number; loops?: number; costPerHr?: number; tokensPerMin?: number }>(
"telemetry",
(d) => d,
{},
);
const [doors, setDoors] = useState<Door[]>([]);
useLiveEvent("door.request", (d) =>
setDoors((cur) => [{ doorId: d.doorId, agentId: d.agentId, action: d.action, summary: d.summary }, ...cur.filter((x) => x.doorId !== d.doorId)].slice(0, 6)),
);
useLiveEvent("door.resolve", (d) => setDoors((cur) => cur.filter((x) => x.doorId !== d.doorId)));
const [agents, setAgents] = useState<Record<string, { status: string; role?: string }>>({});
useLiveEvent("agent.status", (d) => setAgents((cur) => ({ ...cur, [d.agentId]: { status: d.status, role: d.role } })));
// Rolling activity feed — buffered in a ref and flushed on an interval so a
// burst of reasoning deltas doesn't trigger a re-render per token.
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);
};
useLiveEvent("agent.tool.call", (d) =>
push("tool", `${short(d.agentId)}${d.tool}${d.target ? ` ${d.target}` : ""}`, "#5ec8d8"),
);
useLiveEvent("agent.reasoning.delta", (d) => {
const t = d.text.trim();
if (t) push("think", `${short(d.agentId)} · ${t}`, "#9a9aa2");
});
useEffect(() => {
const id = setInterval(() => setFeed(buf.current.slice()), 300);
return () => clearInterval(id);
}, []);
const statusColor = (s: string) => (s === "working" ? "#5ec8d8" : s === "online" ? "#5fd08a" : "#5a5a62");
const pill = (label: string, value: string | number) => (
<div style={{ flex: 1, borderRadius: 10, background: "#101014", border: "1px solid rgba(255,255,255,.06)", padding: "9px 6px", textAlign: "center" }}>
<div style={{ fontSize: 17, fontWeight: 700, color: "#f3f3f5" }}>{value}</div>
<div style={{ fontFamily: mono, fontSize: 8.5, letterSpacing: ".06em", color: "#6a6a72", marginTop: 2 }}>{label}</div>
</div>
);
const agentList = Object.entries(agents);
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 }}>
<span className="cm-blink" style={{ width: 7, height: 7, borderRadius: "50%", background: "#5ec8d8" }} />
<span style={{ fontFamily: mono, fontSize: 12, letterSpacing: ".06em", color: "#cfcfd5", fontWeight: 600 }}>LIVE OBSERVE</span>
</div>
<div style={{ display: "flex", gap: 8 }}>
{pill("DOORS", telemetry.doorsPending ?? 0)}
{pill("LOOPS", telemetry.loops ?? 0)}
{pill("AGENTS", agentList.length)}
</div>
{doors.length > 0 ? (
<Section label="DOORS AWAITING APPROVAL">
{doors.map((d) => (
<div key={d.doorId} style={{ borderRadius: 10, background: "rgba(232,180,101,.08)", border: "1px solid rgba(232,180,101,.3)", padding: "9px 11px" }}>
<div style={{ fontSize: 12.5, fontWeight: 600, color: "#f0d9a8" }}>{d.action}</div>
<div style={{ fontFamily: mono, fontSize: 10, color: "#9a9aa2", marginTop: 2 }}>{short(d.agentId)}{d.summary ? ` · ${d.summary}` : ""}</div>
</div>
))}
</Section>
) : null}
{agentList.length > 0 ? (
<Section label="AGENTS">
{agentList.map(([id, a]) => (
<div key={id} style={{ display: "flex", alignItems: "center", gap: 9, padding: "8px 11px", borderRadius: 10, background: "#101014", border: "1px solid rgba(255,255,255,.06)" }}>
<span style={{ width: 8, height: 8, flex: "none", borderRadius: "50%", background: statusColor(a.status) }} />
<span style={{ flex: 1, minWidth: 0, fontSize: 12.5, color: "#eaeaee", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{a.role || short(id)}</span>
<span style={{ fontFamily: mono, fontSize: 9.5, color: statusColor(a.status) }}>{a.status}</span>
</div>
))}
</Section>
) : null}
<Section label="LIVE ACTIVITY">
{feed.length === 0 ? (
<span style={{ fontFamily: mono, fontSize: 11, color: "#3a3a40" }}>waiting for agent activity</span>
) : (
<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
{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: 1, minWidth: 0, color: "#cfcfd5", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{f.text}</span>
</div>
))}
</div>
)}
</Section>
</div>
);
}
export default ObservePanel;