Observe: per-agent drill-in from the World view
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:
co-authored by
Claude Opus 4.8
parent
c5daf5925c
commit
b9d3fc4f44
@@ -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>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user