Agents page: reorganize into the Agent Command Center (per-agent live metrics)
ci / gates (push) Failing after 13s
ci / rust (push) Has been skipped
ci / sandbox-k8s (push) Has been skipped
ci / frontend (push) Has been skipped
ci / e2e (push) Has been skipped

Replace the single centered "anatomy profile" + resizable chat split with an
operator command center: compact 62px identity strip → 5-tile per-agent metrics
band → three independently-scrolling LIVE · BRAIN · SURFACE columns. Chat moves to
the computer's Chat app; the right computer pullout (DevicePanel) is untouched.

- backend: cm-api/routes/world.rs emits per-agent `telemetry{agentId,tokensPerMin,
  costPerHr,loops,doorsPending}` in the SSE loop, from 4 batched GROUP BY queries
  (usage_events tokens/min + credits/hr, active routines, pending approvals) — all
  real, no migration. taxonomy `telemetry` gains optional agentId; stateKey now
  keys it per-agent so slices don't clobber.
- frontend: new ClawCommandCenter + anatomy-cards (shared cards extracted from
  Dashboard); useAgentTelemetry(agentId) feeds the metric band (Doors amber>0/
  green=0); LIVE column streams the agent's task.update / reasoning.delta /
  tool.call (replaces the mocked VitalsCard heatmap with a live activity chart).
- Dashboard: left region → full-height ClawCommandCenter; chat launcher opens the
  computer Chat app; removed the dead anatomy cluster + unused imports.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-26 11:34:12 -07:00
co-authored by Claude Opus 4.8
parent 731adb6449
commit 11a1f22daa
9 changed files with 2883 additions and 261 deletions
+86
View File
@@ -122,6 +122,76 @@ async fn doors_pending(pool: &PgPool, ws: WorkspaceId) -> i64 {
.unwrap_or(0)
}
/// Live per-agent metrics for the agent command center's metric band.
#[derive(Default)]
struct AgentTele {
tokens_per_min: i64,
cost_per_hr: f64,
loops: i64,
doors: i64,
}
/// Per-agent telemetry for every agent in the workspace, in 4 grouped queries
/// (not N×4): tokens over the last minute, credits over the last hour, active
/// routines, and pending approvals — all keyed by agent id.
async fn agent_telemetry(pool: &PgPool, ws: WorkspaceId) -> std::collections::HashMap<String, AgentTele> {
let mut m: std::collections::HashMap<String, AgentTele> = std::collections::HashMap::new();
let id_of = |r: &sqlx::postgres::PgRow| r.get::<uuid::Uuid, _>("agent_id").to_string();
// tokens in the last minute ≈ tokens/min.
for r in sqlx::query(
"SELECT agent_id, SUM(tokens_in + tokens_out)::bigint AS v FROM usage_events
WHERE workspace_id = $1 AND agent_id IS NOT NULL AND created_at > now() - interval '1 minute'
GROUP BY agent_id",
)
.bind(ws.as_uuid())
.fetch_all(pool)
.await
.unwrap_or_default()
{
m.entry(id_of(&r)).or_default().tokens_per_min = r.get("v");
}
// credits spent in the last hour ≈ spend/hr.
for r in sqlx::query(
"SELECT agent_id, SUM(credits)::float8 AS v FROM usage_events
WHERE workspace_id = $1 AND agent_id IS NOT NULL AND created_at > now() - interval '1 hour'
GROUP BY agent_id",
)
.bind(ws.as_uuid())
.fetch_all(pool)
.await
.unwrap_or_default()
{
m.entry(id_of(&r)).or_default().cost_per_hr = r.get("v");
}
// active routines = loops.
for r in sqlx::query(
"SELECT r.agent_id AS agent_id, count(*)::bigint AS v FROM routines r
JOIN agents a ON a.id = r.agent_id
WHERE a.workspace_id = $1 AND r.status = 'active' GROUP BY r.agent_id",
)
.bind(ws.as_uuid())
.fetch_all(pool)
.await
.unwrap_or_default()
{
m.entry(id_of(&r)).or_default().loops = r.get("v");
}
// pending approvals = doors.
for r in sqlx::query(
"SELECT requested_by_agent AS agent_id, count(*)::bigint AS v FROM approvals
WHERE workspace_id = $1 AND status = 'pending' GROUP BY requested_by_agent",
)
.bind(ws.as_uuid())
.fetch_all(pool)
.await
.unwrap_or_default()
{
m.entry(id_of(&r)).or_default().doors = r.get("v");
}
m
}
/// `GET /api/world/live` — the taxonomy SSE feed.
pub async fn world_live(State(state): State<AppState>, Authed(user): Authed) -> impl IntoResponse {
let pool = state.pool.clone();
@@ -212,6 +282,22 @@ pub async fn world_live(State(state): State<AppState>, Authed(user): Authed) ->
}
}
// Per-agent telemetry → the command-center metric band (real data:
// usage_events tokens/credits, active routines, pending approvals).
let tele = agent_telemetry(&pool, ws).await;
for a in &roster {
let id = a.id.to_string();
let t = tele.get(&id);
yield sse("telemetry", json!({
"agentId": id,
"tokensPerMin": t.map(|x| x.tokens_per_min).unwrap_or(0),
"costPerHr": t.map(|x| x.cost_per_hr).unwrap_or(0.0),
"loops": t.map(|x| x.loops).unwrap_or(0),
"doorsPending": t.map(|x| x.doors).unwrap_or(0),
}));
}
// Workspace-wide telemetry (top-bar pills / Observe system strip).
yield sse(
"telemetry",
json!({ "doorsPending": doors_pending(&pool, ws).await, "loops": runs.len() }),