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
@@ -0,0 +1,592 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: #08080a; }
body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; }
@keyframes cm-blink { 0%,100% { opacity: 1; } 50% { opacity: .25; } }
@keyframes cm-halo { 0% { transform: scale(.7); opacity: .5; } 100% { transform: scale(1.9); opacity: 0; } }
@keyframes cm-type { 0%,100% { opacity: 1; } 50% { opacity: 0; } }
@keyframes cm-bar { 0%,100% { transform: scaleY(.4); } 50% { transform: scaleY(1); } }
@keyframes cm-fade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: rgba(255,255,255,.09); border-radius: 4px; }
::-webkit-scrollbar-track { background: transparent; }
</style>
</helmet>
<div style="width:100%; height:100vh; min-height:680px; background:#08080a; display:flex; flex-direction:column; color:#f3f3f5; overflow:hidden;">
<!-- TOP BAR -->
<div style="height:54px; flex:none; display:flex; align-items:center; gap:14px; padding:0 18px; border-bottom:1px solid rgba(255,255,255,.06); background:linear-gradient(180deg,#0d0d10,#0a0a0c);">
<div style="display:flex; align-items:center; gap:10px;">
<svg width="22" height="22" viewBox="0 0 22 22" fill="none">
<path d="M11 3 L18.5 17 L3.5 17 Z" stroke="#ff6f61" stroke-width="1.3" stroke-linejoin="round" opacity="0.55"></path>
<circle cx="11" cy="3.5" r="2.4" fill="#ff6f61"></circle><circle cx="18" cy="17" r="2.4" fill="#ff6f61"></circle><circle cx="4" cy="17" r="2.4" fill="#ff6f61"></circle>
</svg>
<span style="font-size:15px; font-weight:700; letter-spacing:-.01em;">Clawmates</span>
</div>
<div style="width:1px; height:22px; background:rgba(255,255,255,.08);"></div>
<div style="display:flex; align-items:center; gap:8px; font-family:'JetBrains Mono',monospace; font-size:12px;">
<span style="color:#6a6a72;">Large World</span>
<span style="color:#3a3a40;">/</span>
<span style="color:#9a9aa2;">Agents</span>
<span style="color:#3a3a40;">/</span>
<span style="color:#fff; background:rgba(255,111,97,.14); border:1px solid rgba(255,111,97,.3); padding:3px 8px; border-radius:6px;">{{ selName }}</span>
</div>
<div style="flex:1;"></div>
<div style="display:flex; align-items:center; gap:7px; font-family:'JetBrains Mono',monospace; font-size:11px; color:#5ec8d8; padding:5px 10px; border:1px solid rgba(94,200,216,.25); border-radius:7px; background:rgba(94,200,216,.06);">
<span style="width:7px; height:7px; border-radius:50%; background:#5ec8d8; {{ blinkAnim }}"></span>{{ selStatusLabel }}
</div>
<div style="display:flex; align-items:center; gap:5px; font-family:'JetBrains Mono',monospace; font-size:11px; color:#8a8a92;">
<span style="color:#e8b465;"></span> {{ selTok }} tok/min
</div>
<div style="width:32px; height:32px; border-radius:8px; background:linear-gradient(135deg,#ff8a7a,#ff5f57); display:flex; align-items:center; justify-content:center; font-size:13px; font-weight:700; color:#2a0d0a;">O</div>
</div>
<!-- BODY -->
<div style="flex:1; display:flex; min-height:0;">
<!-- ICON RAIL -->
<div style="width:54px; flex:none; border-right:1px solid rgba(255,255,255,.06); background:#0a0a0c; display:flex; flex-direction:column; align-items:center; padding:12px 0; gap:6px;">
<div style="width:42px; height:46px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; border-radius:9px; color:#5a5a62; cursor:pointer;">
<svg width="19" height="19" viewBox="0 0 20 20"><circle cx="10" cy="10" r="7.5" stroke="currentColor" stroke-width="1.4" fill="none"></circle><circle cx="10" cy="10" r="2.4" fill="currentColor"></circle></svg>
<span style="font-family:'JetBrains Mono',monospace; font-size:7px; letter-spacing:.05em;">WORLD</span>
</div>
<div style="position:relative; width:42px; height:46px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; border-radius:9px; color:#ff6f61; background:rgba(255,111,97,.1); cursor:pointer;">
<span style="position:absolute; left:0; top:7px; bottom:7px; width:3px; border-radius:0 3px 3px 0; background:#ff6f61;"></span>
<svg width="19" height="19" viewBox="0 0 20 20"><rect x="4" y="4" width="12" height="12" rx="3.5" stroke="currentColor" stroke-width="1.4" fill="none"></rect><circle cx="10" cy="10" r="2.2" fill="currentColor"></circle></svg>
<span style="font-family:'JetBrains Mono',monospace; font-size:7px; letter-spacing:.05em;">AGENT</span>
</div>
<div style="width:42px; height:46px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; border-radius:9px; color:#5a5a62; cursor:pointer;">
<svg width="19" height="19" viewBox="0 0 20 20"><rect x="3.5" y="4" width="13" height="5" rx="1.4" stroke="currentColor" stroke-width="1.3" fill="none"></rect><rect x="3.5" y="11" width="13" height="5" rx="1.4" stroke="currentColor" stroke-width="1.3" fill="none"></rect></svg>
<span style="font-family:'JetBrains Mono',monospace; font-size:7px; letter-spacing:.05em;">INFRA</span>
</div>
<div style="flex:1;"></div>
<div style="width:30px; height:30px; border-radius:8px; border:1px dashed rgba(255,255,255,.16); display:flex; align-items:center; justify-content:center; color:#ff6f61; font-size:18px; font-weight:300; cursor:pointer;">+</div>
</div>
<!-- AGENT LIST -->
<div style="width:222px; flex:none; border-right:1px solid rgba(255,255,255,.06); background:#0b0b0e; display:flex; flex-direction:column; min-height:0;">
<div style="padding:14px 14px 10px;">
<div style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.12em; color:#5a5a62;">2 AGENTS · LIVE</div>
<div style="font-size:17px; font-weight:700; margin-top:3px;">Agents</div>
</div>
<div style="flex:1; overflow-y:auto; padding:6px;">
<div style="display:flex; flex-direction:column; gap:3px;">
<sc-for list="{{ agents }}" as="a" hint-placeholder-count="2">
<div style="display:flex; align-items:flex-start; gap:10px; padding:9px 10px; border-radius:10px; cursor:pointer; position:relative; background:{{ a.rowBg }}; border:1px solid {{ a.rowBorder }};" onClick="{{ a.onSelect }}">
<sc-if value="{{ a.selected }}" hint-placeholder-val="{{ true }}"><span style="position:absolute; left:0; top:9px; bottom:9px; width:3px; border-radius:0 3px 3px 0; background:#ff6f61;"></span></sc-if>
<div style="position:relative; width:32px; height:32px; flex:none;">
<div style="width:32px; height:32px; border-radius:9px; background:{{ a.grad }}; display:flex; align-items:center; justify-content:center; font-size:13px; font-weight:700; color:{{ a.ink }};">{{ a.initial }}</div>
<span style="position:absolute; right:-2px; bottom:-2px; width:10px; height:10px; border-radius:50%; background:{{ a.statusColor }}; border:2px solid #0b0b0e;"></span>
</div>
<div style="flex:1; min-width:0;">
<div style="font-size:13px; font-weight:600; color:{{ a.labelColor }};">{{ a.name }}</div>
<div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:{{ a.roleColor }};">{{ a.role }}</div>
<div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:{{ a.statusColor }}; margin-top:4px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">{{ a.action }}</div>
</div>
</div>
</sc-for>
</div>
</div>
<div style="flex:none; padding:10px 12px; border-top:1px solid rgba(255,255,255,.06);">
<div style="display:flex; align-items:center; justify-content:center; gap:7px; height:32px; border-radius:8px; border:1px solid rgba(94,200,216,.3); color:#5ec8d8; font-size:12px; font-weight:600; cursor:pointer;">⊕ Add to team</div>
</div>
</div>
<!-- COMMAND CENTER -->
<div data-screen-label="Agent Command Center" style="flex:1; min-width:0; display:flex; flex-direction:column; min-height:0; background:radial-gradient(130% 100% at 30% 0%, #100e13 0%, #08080a 60%);">
<!-- IDENTITY HEADER STRIP -->
<div style="flex:none; display:flex; align-items:center; gap:16px; padding:16px 20px 14px; border-bottom:1px solid rgba(255,255,255,.06);">
<div style="position:relative; width:62px; height:62px; flex:none;">
<div style="position:absolute; inset:-5px; border-radius:50%; border:1.5px solid rgba(255,111,97,.4);"></div>
<div style="position:absolute; inset:0; border-radius:50%; background:rgba(255,111,97,.3); {{ haloAnim }}"></div>
<div style="position:relative; width:62px; height:62px; border-radius:50%; background:{{ selGrad }}; display:flex; align-items:center; justify-content:center; font-size:24px; font-weight:700; color:{{ selInk }}; box-shadow:0 0 30px rgba(255,111,97,.4);">{{ selInitial }}</div>
</div>
<div style="min-width:0;">
<div style="font-size:24px; font-weight:700; letter-spacing:-.02em; line-height:1.1;">{{ selName }}</div>
<div style="font-family:'JetBrains Mono',monospace; font-size:11px; color:#6a6a72; margin-top:3px;">{{ selRole }} · {{ selSpec }}</div>
</div>
<div style="flex:1;"></div>
<div style="display:flex; align-items:center; gap:8px; padding:7px 11px; border-radius:9px; background:#101014; border:1px solid rgba(255,255,255,.08);">
<span style="width:7px; height:7px; border-radius:50%; background:#ff6f61;"></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:11px; color:#cfcfd5;">{{ selModel }}</span>
</div>
<div style="display:flex; align-items:center; gap:7px; padding:8px 13px; border-radius:9px; background:linear-gradient(135deg,#ff8a7a,#ff5f57); color:#2a0d0a; font-size:12px; font-weight:700; cursor:pointer;">Edit brain →</div>
<div style="width:34px; height:34px; border-radius:9px; border:1px solid rgba(255,255,255,.1); display:flex; align-items:center; justify-content:center; color:#9a9aa2; cursor:pointer;"><svg width="16" height="16" viewBox="0 0 18 18"><circle cx="9" cy="9" r="2.4" fill="currentColor"></circle><path d="M9 1.5v2.2M9 14.3v2.2M1.5 9h2.2M14.3 9h2.2M3.6 3.6l1.6 1.6M12.8 12.8l1.6 1.6M14.4 3.6l-1.6 1.6M5.2 12.8l-1.6 1.6" stroke="currentColor" stroke-width="1.4"></path></svg></div>
</div>
<!-- METRICS BAND -->
<div style="flex:none; display:flex; gap:12px; padding:14px 20px;">
<div style="flex:1.4; border-radius:11px; background:#0d0d10; border:1px solid rgba(94,200,216,.22); padding:11px 14px; display:flex; align-items:center; gap:12px;">
<div style="min-width:0;">
<div style="font-family:'JetBrains Mono',monospace; font-size:9px; letter-spacing:.1em; color:#6a6a72;">THROUGHPUT</div>
<div style="display:flex; align-items:baseline; gap:5px; margin-top:4px;"><span style="font-size:22px; font-weight:700; color:#5ec8d8; letter-spacing:-.01em;">{{ selTok }}</span><span style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72;">tok/min</span></div>
</div>
<span style="flex:1;"></span>
<span style="display:flex; align-items:flex-end; gap:2px; height:30px;">
<span style="width:3px; background:#2c5a64; height:40%; border-radius:1px;"></span>
<span style="width:3px; background:#3a7e8c; height:68%; border-radius:1px;"></span>
<span style="width:3px; background:#4aa3b8; height:52%; border-radius:1px; transform-origin:bottom; {{ barAnim }}"></span>
<span style="width:3px; background:#5ec8d8; height:88%; border-radius:1px; transform-origin:bottom; {{ barAnim2 }}"></span>
<span style="width:3px; background:#4aa3b8; height:64%; border-radius:1px;"></span>
<span style="width:3px; background:#3a7e8c; height:46%; border-radius:1px;"></span>
<span style="width:3px; background:#5ec8d8; height:76%; border-radius:1px;"></span>
</span>
</div>
<div style="flex:1; border-radius:11px; background:#0d0d10; border:1px solid rgba(255,255,255,.06); padding:11px 14px;">
<div style="font-family:'JetBrains Mono',monospace; font-size:9px; letter-spacing:.1em; color:#6a6a72;">SPEND</div>
<div style="display:flex; align-items:baseline; gap:5px; margin-top:4px;"><span style="font-size:22px; font-weight:700; color:#e8b465; letter-spacing:-.01em;">{{ selSpend }}</span><span style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72;">/hr</span></div>
</div>
<div style="flex:1; border-radius:11px; background:#0d0d10; border:1px solid rgba(255,255,255,.06); padding:11px 14px;">
<div style="font-family:'JetBrains Mono',monospace; font-size:9px; letter-spacing:.1em; color:#6a6a72;">LOOPS</div>
<div style="display:flex; align-items:baseline; gap:5px; margin-top:4px;"><span style="font-size:22px; font-weight:700; color:#5fd08a; letter-spacing:-.01em;">{{ selLoops }}</span><span style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72;">running</span></div>
</div>
<div style="flex:1; border-radius:11px; background:#0d0d10; border:1px solid rgba(255,255,255,.06); padding:11px 14px;">
<div style="font-family:'JetBrains Mono',monospace; font-size:9px; letter-spacing:.1em; color:#6a6a72;">MEMORY</div>
<div style="display:flex; align-items:baseline; gap:5px; margin-top:4px;"><span style="font-size:22px; font-weight:700; color:#cfcfd5; letter-spacing:-.01em;">{{ selMem }}</span><span style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72;">notes</span></div>
</div>
<div style="flex:1; border-radius:11px; background:#0d0d10; border:1px solid {{ doorsBorder }}; padding:11px 14px;">
<div style="font-family:'JetBrains Mono',monospace; font-size:9px; letter-spacing:.1em; color:{{ doorsLabelColor }};">DOORS</div>
<div style="display:flex; align-items:baseline; gap:5px; margin-top:4px;"><span style="font-size:22px; font-weight:700; color:{{ doorsValColor }}; letter-spacing:-.01em;">{{ doorsLive }}</span><span style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72;">pending</span></div>
</div>
</div>
<!-- THREE COLUMNS -->
<div style="flex:1; min-height:0; display:flex; gap:14px; padding:0 20px 16px;">
<!-- LIVE COLUMN -->
<div style="flex:1.15; min-width:0; display:flex; flex-direction:column; gap:12px; overflow-y:auto;">
<div style="flex:none; border-radius:13px; border:1px solid rgba(94,200,216,.24); background:linear-gradient(180deg,#0c1416,#0b0e0f); overflow:hidden;">
<div style="display:flex; align-items:center; gap:8px; padding:11px 14px; border-bottom:1px solid rgba(255,255,255,.05);">
<span style="width:7px; height:7px; border-radius:50%; background:#5ec8d8; {{ blinkAnim }}"></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.12em; color:#5ec8d8;">WORKING ON NOW</span>
<span style="flex:1;"></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; color:#6a6a72;">{{ taskElapsed }}</span>
</div>
<div style="padding:13px 14px;">
<div style="font-size:15px; font-weight:600; margin-bottom:8px; line-height:1.35;">{{ taskTitle }} <span style="font-family:'JetBrains Mono',monospace; color:#ff8a7a;">{{ taskCode }}</span></div>
<div style="display:flex; flex-direction:column; gap:5px; font-family:'JetBrains Mono',monospace; font-size:11px; line-height:1.6;">
<sc-for list="{{ taskSteps }}" as="st" hint-placeholder-count="3">
<div style="color:#7a7a82;"><span style="color:{{ st.iconColor }};">{{ st.icon }}</span> {{ st.text }} <span style="color:#5a5a62;">{{ st.meta }}</span><sc-if value="{{ st.caret }}" hint-placeholder-val="{{ false }}"><span style="display:inline-block; width:6px; height:12px; background:#5ec8d8; margin-left:2px; vertical-align:-2px; {{ caretAnim }}"></span></sc-if></div>
</sc-for>
</div>
</div>
</div>
<div style="flex:1; min-height:160px; border-radius:13px; border:1px solid rgba(255,255,255,.08); background:#070708; overflow:hidden; display:flex; flex-direction:column;">
<div style="flex:none; display:flex; align-items:center; gap:8px; padding:10px 13px; border-bottom:1px solid rgba(255,255,255,.06);">
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.12em; color:#5fd08a;">▌ REASONING STREAM</span>
<span style="flex:1;"></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; color:#5a5a62;">{{ selModelShort }} · {{ reasoningTime }}</span>
</div>
<div style="flex:1; min-height:0; overflow-y:auto; padding:12px 14px; font-family:'JetBrains Mono',monospace; font-size:11px; line-height:1.7;">
<sc-for list="{{ reasoning }}" as="r" hint-placeholder-count="4">
<div style="color:#9a9aa2; margin-bottom:7px;"><span style="color:{{ r.tagColor }};">{{ r.tag }}</span> {{ r.text }}<sc-if value="{{ r.caret }}" hint-placeholder-val="{{ false }}"><span style="display:inline-block; width:6px; height:12px; background:#5fd08a; margin-left:3px; vertical-align:-2px; {{ caretAnim }}"></span></sc-if></div>
</sc-for>
</div>
</div>
<div style="flex:none; border-radius:13px; border:1px solid rgba(255,255,255,.07); background:#0d0d10; padding:13px 14px;">
<div style="display:flex; align-items:center; gap:8px; margin-bottom:11px;">
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.12em; color:#5fd08a;">● ACTIVITY · LIVE</span>
<span style="flex:1;"></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; color:#6a6a72;">{{ selCommits }} commits · {{ selHot }} hot</span>
</div>
<div style="display:flex; align-items:flex-end; gap:3px; height:46px;">
<span style="flex:1; background:#1e3a2a; border-radius:2px; height:30%;"></span>
<span style="flex:1; background:#2e6e44; border-radius:2px; height:55%;"></span>
<span style="flex:1; background:#3a9457; border-radius:2px; height:42%;"></span>
<span style="flex:1; background:#46c46a; border-radius:2px; height:78%;"></span>
<span style="flex:1; background:#e8b465; border-radius:2px; height:62%; transform-origin:bottom; {{ barAnim }}"></span>
<span style="flex:1; background:#2e6e44; border-radius:2px; height:48%;"></span>
<span style="flex:1; background:#3a9457; border-radius:2px; height:70%;"></span>
<span style="flex:1; background:#46c46a; border-radius:2px; height:90%; transform-origin:bottom; {{ barAnim2 }}"></span>
<span style="flex:1; background:#2e6e44; border-radius:2px; height:36%;"></span>
<span style="flex:1; background:#3a9457; border-radius:2px; height:58%;"></span>
<span style="flex:1; background:#1e3a2a; border-radius:2px; height:44%;"></span>
<span style="flex:1; background:#46c46a; border-radius:2px; height:66%;"></span>
</div>
</div>
</div>
<!-- BRAIN COLUMN -->
<div style="flex:1; min-width:0; display:flex; flex-direction:column; gap:12px; overflow-y:auto;">
<div style="flex:none; border-radius:13px; background:#0d0d10; border:1px solid rgba(255,111,97,.22); overflow:hidden;">
<div style="display:flex; align-items:center; gap:8px; padding:12px 13px; cursor:pointer;" onClick="{{ togglePrompt }}">
<span style="width:18px; height:18px; border-radius:5px; background:rgba(255,111,97,.16); display:flex; align-items:center; justify-content:center;"><svg width="11" height="11" viewBox="0 0 16 16"><path d="M4 2h6l2 2v10H4z" fill="none" stroke="#ff6f61" stroke-width="1.3" stroke-linejoin="round"></path><path d="M6 6h4M6 9h4" stroke="#ff6f61" stroke-width="1.2"></path></svg></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.08em; color:#ff8a7a;">SYSTEM PROMPT</span>
<span style="flex:1;"></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#5a5a62;">{{ promptChars }} ch</span>
<span style="font-family:'JetBrains Mono',monospace; font-size:11px; color:#ff8a7a;">{{ promptChevron }}</span>
</div>
<sc-if value="{{ promptOpen }}" hint-placeholder-val="{{ false }}">
<div style="padding:0 14px 13px; animation:cm-fade .2s ease;">
<div style="max-height:148px; overflow-y:auto; padding:11px 12px; border-radius:9px; background:#08080a; border:1px solid rgba(255,255,255,.06); font-family:'JetBrains Mono',monospace; font-size:11px; line-height:1.7; color:#9a9aa2; white-space:pre-wrap;">{{ promptText }}</div>
</div>
</sc-if>
</div>
<div style="flex:none; border-radius:13px; background:#0d0d10; border:1px solid rgba(201,138,240,.22); padding:12px 13px;">
<div style="display:flex; align-items:center; gap:8px; margin-bottom:10px;">
<span style="width:18px; height:18px; border-radius:5px; background:rgba(201,138,240,.16); display:flex; align-items:center; justify-content:center;"><svg width="11" height="11" viewBox="0 0 18 18"><circle cx="9" cy="6" r="3.4" fill="#c98af0"></circle><path d="M3 16c0-3.3 2.7-6 6-6s6 2.7 6 6" fill="#c98af0"></path></svg></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.08em; color:#c98af0;">PERSONALITY</span>
</div>
<div style="display:flex; flex-wrap:wrap; gap:5px;">
<sc-for list="{{ personality }}" as="p" hint-placeholder-count="3">
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; color:#cfcfd5; padding:4px 9px; border-radius:6px; background:rgba(201,138,240,.1); border:1px solid rgba(201,138,240,.18);">{{ p }}</span>
</sc-for>
</div>
</div>
<div style="flex:none; border-radius:13px; background:#0d0d10; border:1px solid rgba(255,111,97,.18); padding:12px 13px;">
<div style="display:flex; align-items:center; gap:8px; margin-bottom:10px;">
<span style="width:18px; height:18px; border-radius:5px; background:rgba(255,111,97,.16); display:flex; align-items:center; justify-content:center;"><svg width="11" height="11" viewBox="0 0 18 18"><path d="M10 1 L3 10 H8 L7 17 L15 7 H9 Z" fill="#ff6f61"></path></svg></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.08em; color:#ff8a7a;">SKILLS</span>
<span style="flex:1;"></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#5a5a62;">{{ skillsCount }}</span>
</div>
<div style="display:flex; flex-wrap:wrap; gap:5px;">
<sc-for list="{{ skills }}" as="sk" hint-placeholder-count="4">
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; color:#cfcfd5; padding:4px 9px; border-radius:6px; background:rgba(255,255,255,.05);">{{ sk }}</span>
</sc-for>
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; color:#8a8a92; padding:4px 9px; border-radius:6px; background:rgba(255,255,255,.03);">{{ skillsMore }}</span>
</div>
</div>
<div style="flex:none; border-radius:13px; background:#0d0d10; border:1px solid rgba(232,196,106,.2); padding:12px 13px;">
<div style="display:flex; align-items:center; gap:8px; margin-bottom:10px;">
<span style="width:18px; height:18px; border-radius:5px; background:rgba(232,196,106,.16); display:flex; align-items:center; justify-content:center;"><svg width="11" height="11" viewBox="0 0 18 18"><rect x="3" y="3" width="12" height="12" rx="2.5" fill="none" stroke="#e8b465" stroke-width="1.5"></rect><rect x="6.5" y="6.5" width="5" height="5" rx="1" fill="#e8b465"></rect></svg></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.08em; color:#e8b465;">CAPABILITIES</span>
</div>
<div style="display:flex; flex-wrap:wrap; gap:5px;">
<sc-for list="{{ capabilities }}" as="c" hint-placeholder-count="3">
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; color:#cfcfd5; padding:4px 9px; border-radius:6px; background:rgba(255,255,255,.05);">{{ c }}</span>
</sc-for>
</div>
</div>
</div>
<!-- SURFACE COLUMN -->
<div style="flex:1; min-width:0; display:flex; flex-direction:column; gap:12px; overflow-y:auto;">
<div style="flex:none; border-radius:13px; background:#0d0d10; border:1px solid rgba(94,200,216,.22); padding:12px 13px;">
<div style="display:flex; align-items:center; gap:8px; margin-bottom:10px;">
<span style="width:18px; height:18px; border-radius:5px; background:rgba(94,200,216,.16); display:flex; align-items:center; justify-content:center;"><svg width="11" height="11" viewBox="0 0 18 18"><path d="M4 3v12M4 4h8l-2 3 2 3H4" fill="none" stroke="#5ec8d8" stroke-width="1.5" stroke-linejoin="round"></path></svg></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.08em; color:#5ec8d8;">TOOLS · DOORS</span>
</div>
<div style="display:flex; flex-direction:column; gap:6px; font-family:'JetBrains Mono',monospace; font-size:11px;">
<sc-for list="{{ tools }}" as="t" hint-placeholder-count="4">
<div style="display:flex; align-items:center;"><span style="flex:1; color:{{ t.nameColor }};">{{ t.name }}</span><span style="color:{{ t.stateColor }};">{{ t.state }}</span></div>
</sc-for>
</div>
<sc-if value="{{ showDoor }}" hint-placeholder-val="{{ true }}">
<div style="margin-top:11px; border-radius:10px; border:1px solid rgba(232,196,106,.4); background:linear-gradient(180deg,#1a1408,#12100a); overflow:hidden; animation:cm-fade .2s ease;">
<div style="height:3px; background:linear-gradient(90deg,#e8b465,#ff6f61);"></div>
<div style="padding:11px 12px;">
<div style="display:flex; align-items:center; gap:7px; margin-bottom:8px;">
<span style="font-family:'JetBrains Mono',monospace; font-size:9px; letter-spacing:.1em; color:#e8b465;">§15 DOOR · APPROVAL</span>
</div>
<div style="font-size:13px; font-weight:600; margin-bottom:3px;">{{ doorTitle }}</div>
<div style="font-family:'JetBrains Mono',monospace; font-size:10px; color:#8a8a92; line-height:1.6; margin-bottom:11px;">{{ doorDetail }}</div>
<div style="display:flex; gap:7px;">
<div style="flex:1; display:flex; align-items:center; justify-content:center; height:31px; border-radius:8px; background:linear-gradient(135deg,#ffb27a,#e8b465); color:#2a1d05; font-size:12px; font-weight:700; cursor:pointer;" onClick="{{ approveDoor }}">Approve</div>
<div style="flex:1; display:flex; align-items:center; justify-content:center; height:31px; border-radius:8px; border:1px solid rgba(255,255,255,.14); color:#cfcfd5; font-size:12px; font-weight:600; cursor:pointer;">Review</div>
<div style="width:34px; display:flex; align-items:center; justify-content:center; height:31px; border-radius:8px; border:1px solid rgba(255,255,255,.1); color:#8a8a92; cursor:pointer;" onClick="{{ approveDoor }}"></div>
</div>
</div>
</div>
</sc-if>
<sc-if value="{{ doorClear }}" hint-placeholder-val="{{ false }}">
<div style="margin-top:11px; display:flex; align-items:center; gap:7px; padding:9px 11px; border-radius:9px; background:rgba(95,208,138,.07); border:1px solid rgba(95,208,138,.22); font-family:'JetBrains Mono',monospace; font-size:10px; color:#5fd08a;">✓ no doors awaiting approval</div>
</sc-if>
</div>
<div style="flex:none; border-radius:13px; background:#0d0d10; border:1px solid rgba(95,208,138,.2); padding:12px 13px;">
<div style="display:flex; align-items:center; gap:8px; margin-bottom:10px;">
<span style="width:18px; height:18px; border-radius:5px; background:rgba(95,208,138,.16); display:flex; align-items:center; justify-content:center;"><svg width="11" height="11" viewBox="0 0 18 18"><rect x="3" y="3" width="12" height="12" rx="2" fill="none" stroke="#5fd08a" stroke-width="1.6"></rect><path d="M6 7h6M6 11h4" stroke="#5fd08a" stroke-width="1.4"></path></svg></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.08em; color:#5fd08a;">MEMORY</span>
</div>
<div style="display:flex; flex-direction:column; gap:6px; font-family:'JetBrains Mono',monospace; font-size:11px; color:#9a9aa2;">
<sc-for list="{{ memory }}" as="m" hint-placeholder-count="3">
<div style="display:flex;"><span style="flex:1;">{{ m.k }}</span><span style="color:#cfcfd5;">{{ m.v }}</span></div>
</sc-for>
</div>
</div>
<div style="flex:none; border-radius:13px; background:#0d0d10; border:1px solid rgba(111,208,192,.2); padding:12px 13px;">
<div style="display:flex; align-items:center; gap:8px; margin-bottom:10px;">
<span style="width:18px; height:18px; border-radius:5px; background:rgba(111,208,192,.16); display:flex; align-items:center; justify-content:center;"><svg width="11" height="11" viewBox="0 0 18 18"><path d="M9 2l5 2v4c0 4-2.5 6.5-5 8-2.5-1.5-5-4-5-8V4z" fill="none" stroke="#6fd0c0" stroke-width="1.5" stroke-linejoin="round"></path></svg></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.08em; color:#6fd0c0;">SAFETY · §15</span>
</div>
<div style="display:flex; flex-direction:column; gap:6px; font-family:'JetBrains Mono',monospace; font-size:11px; color:#9a9aa2;">
<sc-for list="{{ safety }}" as="sf" hint-placeholder-count="3">
<div style="display:flex;"><span style="flex:1;">{{ sf.k }}</span><span style="color:{{ sf.color }};">{{ sf.v }}</span></div>
</sc-for>
</div>
</div>
</div>
</div>
</div>
<!-- COMPUTER PULLOUT PANEL -->
<sc-if value="{{ showComputer }}" hint-placeholder-val="{{ true }}">
<div style="width:344px; flex:none; border-left:1px solid rgba(255,255,255,.06); background:#0b0b0e; display:flex; flex-direction:column; min-height:0; animation:cm-fade .25s ease;">
<div style="flex:none; display:flex; align-items:center; gap:9px; padding:13px 14px 11px; border-bottom:1px solid rgba(255,255,255,.06);">
<span style="width:7px; height:7px; border-radius:50%; background:#ff6f61;"></span>
<span style="font-size:13px; font-weight:700;">{{ selName }}'s Computer</span>
<span style="flex:1;"></span>
<div style="display:flex; align-items:center; gap:8px; color:#5a5a62;">
<svg width="15" height="15" viewBox="0 0 18 18" style="cursor:pointer; color:#ff6f61;"><path d="M3 4a1.5 1.5 0 011.5-1.5h9A1.5 1.5 0 0115 4v6a1.5 1.5 0 01-1.5 1.5H7l-4 3.5V4z" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"></path></svg>
<svg width="15" height="15" viewBox="0 0 18 18" style="cursor:pointer;"><rect x="2" y="3" width="14" height="9" rx="1.4" fill="none" stroke="currentColor" stroke-width="1.3"></rect><path d="M6 15h6" stroke="currentColor" stroke-width="1.3"></path></svg>
<svg width="15" height="15" viewBox="0 0 18 18" style="cursor:pointer;"><rect x="4" y="2.5" width="10" height="13" rx="1.6" fill="none" stroke="currentColor" stroke-width="1.3"></rect></svg>
<svg width="15" height="15" viewBox="0 0 18 18" style="cursor:pointer;"><rect x="5.5" y="2" width="7" height="14" rx="1.6" fill="none" stroke="currentColor" stroke-width="1.3"></rect><path d="M8 13.5h2" stroke="currentColor" stroke-width="1.3"></path></svg>
<span style="width:1px; height:14px; background:rgba(255,255,255,.1);"></span>
<svg width="14" height="14" viewBox="0 0 16 16" style="cursor:pointer;"><path d="M3.5 3.5l9 9M12.5 3.5l-9 9" stroke="currentColor" stroke-width="1.4"></path></svg>
</div>
</div>
<div style="flex:none; display:flex; align-items:center; gap:7px; padding:9px 16px 0; font-family:'JetBrains Mono',monospace; font-size:10px; color:#5ec8d8;">
<span style="width:6px; height:6px; border-radius:50%; background:#5ec8d8; {{ blinkAnim }}"></span>sandbox live · no network
</div>
<div style="flex:1; overflow-y:auto; padding:16px;">
<div style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.12em; color:#5a5a62; margin-bottom:13px;">APPS</div>
<div style="display:grid; grid-template-columns:repeat(4,1fr); gap:14px 6px; margin-bottom:22px;">
<div style="display:flex; flex-direction:column; align-items:center; gap:6px; cursor:pointer;"><div style="width:48px; height:48px; border-radius:13px; background:#fff; display:flex; align-items:center; justify-content:center; box-shadow:0 3px 10px rgba(0,0,0,.4);"><svg width="26" height="26" viewBox="0 0 26 26"><circle cx="13" cy="13" r="11" fill="#fff" stroke="#e0e0e0"></circle><circle cx="13" cy="13" r="4.4" fill="#4a90e2"></circle><path d="M13 8.6 H24" stroke="#ea4335" stroke-width="3.6"></path><path d="M9.2 11 L4 3.4" stroke="#34a853" stroke-width="3.6"></path><path d="M13 17.4 L7.5 22.5" stroke="#fbbc05" stroke-width="3.6"></path></svg></div><span style="font-size:10px; color:#b5b5bd;">Browser</span></div>
<div style="display:flex; flex-direction:column; align-items:center; gap:6px; cursor:pointer;"><div style="width:48px; height:48px; border-radius:13px; background:#fff; display:flex; align-items:center; justify-content:center; box-shadow:0 3px 10px rgba(0,0,0,.4);"><svg width="22" height="22" viewBox="0 0 22 22"><rect x="9" y="2" width="4" height="11" rx="2" fill="#36c5f0"></rect><rect x="9" y="13" width="4" height="7" rx="2" fill="#2eb67d"></rect><rect x="2" y="9" width="11" height="4" rx="2" fill="#ecb22e"></rect><rect x="9" y="9" width="11" height="4" rx="2" fill="#e01e5a"></rect></svg></div><span style="font-size:10px; color:#b5b5bd;">Slack</span></div>
<div style="display:flex; flex-direction:column; align-items:center; gap:6px; cursor:pointer;"><div style="width:48px; height:48px; border-radius:13px; background:#070708; border:1px solid rgba(255,255,255,.1); display:flex; align-items:center; justify-content:center;"><svg width="22" height="22" viewBox="0 0 22 22"><rect x="2" y="3" width="18" height="15" rx="2" fill="none" stroke="#5fd08a" stroke-width="1.4"></rect><path d="M5 8l3 2.5-3 2.5M10.5 14h5" stroke="#5fd08a" stroke-width="1.4" fill="none"></path></svg></div><span style="font-size:10px; color:#b5b5bd;">Terminal</span></div>
<div style="display:flex; flex-direction:column; align-items:center; gap:6px; cursor:pointer;"><div style="width:48px; height:48px; border-radius:13px; background:linear-gradient(135deg,#c98af0,#7a4ad8); display:flex; align-items:center; justify-content:center; box-shadow:0 3px 10px rgba(0,0,0,.4);"><svg width="22" height="22" viewBox="0 0 22 22"><path d="M11 2 L18 8 L13 20 L6 16 L4 8 Z" fill="#fff" opacity=".92"></path><path d="M11 2 L13 20 M4 8 L13 20" stroke="#c98af0" stroke-width="1" fill="none"></path></svg></div><span style="font-size:10px; color:#b5b5bd;">Obsidian</span></div>
<div style="display:flex; flex-direction:column; align-items:center; gap:6px; cursor:pointer;"><div style="width:48px; height:48px; border-radius:13px; background:#070708; border:1px solid rgba(255,255,255,.1); display:flex; align-items:center; justify-content:center;"><svg width="22" height="22" viewBox="0 0 22 22"><path d="M3 5a2 2 0 012-2h12a2 2 0 012 2v8a2 2 0 01-2 2H8l-4 4v-4H5a2 2 0 01-2-2z" fill="none" stroke="#ff6f61" stroke-width="1.4" stroke-linejoin="round"></path></svg></div><span style="font-size:10px; color:#b5b5bd;">Chat</span></div>
<div style="display:flex; flex-direction:column; align-items:center; gap:6px;"><div style="width:48px; height:48px; border-radius:13px; border:1.5px dashed rgba(255,255,255,.18); display:flex; align-items:center; justify-content:center; color:#ff6f61; font-size:22px; font-weight:300; cursor:pointer;">+</div><span style="font-size:10px; color:#8a8a92;">Add Apps</span></div>
</div>
<div style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.12em; color:#5a5a62; margin-bottom:11px;">NOW RUNNING</div>
<sc-for list="{{ running }}" as="r" hint-placeholder-count="2">
<div style="padding:10px 11px; border-radius:10px; background:#101014; border:1px solid rgba(255,255,255,.06); margin-bottom:8px;">
<div style="display:flex; align-items:center; gap:7px; margin-bottom:6px;">
<span style="width:6px; height:6px; border-radius:50%; background:{{ r.dot }}; {{ r.dotAnim }}"></span>
<span style="font-size:12px; font-weight:600; color:#e6e6ea;">{{ r.name }}</span>
<span style="flex:1;"></span>
<span style="font-family:'JetBrains Mono',monospace; font-size:9px; color:{{ r.metaColor }};">{{ r.meta }}</span>
</div>
<sc-if value="{{ r.hasBar }}" hint-placeholder-val="{{ false }}">
<div style="height:4px; border-radius:2px; background:rgba(255,255,255,.08); overflow:hidden;"><div style="width:{{ r.bar }}; height:100%; background:linear-gradient(90deg,#5ec8d8,#4aa3b8);"></div></div>
</sc-if>
</div>
</sc-for>
</div>
<div style="flex:none; margin:0 12px 14px; padding:12px; border-radius:14px; background:#121216; border:1px solid rgba(255,255,255,.07); display:flex; justify-content:space-around;">
<div style="display:flex; flex-direction:column; align-items:center; gap:6px; cursor:pointer;"><div style="width:40px; height:40px; border-radius:11px; background:linear-gradient(135deg,#ff8a7a,#ff5f57); display:flex; align-items:center; justify-content:center;"><svg width="18" height="18" viewBox="0 0 18 18"><path d="M10 1 L3 10 H8 L7 17 L15 7 H9 Z" fill="#fff"></path></svg></div><span style="font-size:10px; color:#b5b5bd;">Skills</span></div>
<div style="display:flex; flex-direction:column; align-items:center; gap:6px; cursor:pointer;"><div style="width:40px; height:40px; border-radius:11px; background:linear-gradient(135deg,#ff8a7a,#ff5f57); display:flex; align-items:center; justify-content:center;"><svg width="18" height="18" viewBox="0 0 18 18"><path d="M2 5a1.5 1.5 0 011.5-1.5H7l1.5 1.5h6A1.5 1.5 0 0116 6.5v7A1.5 1.5 0 0114.5 15h-11A1.5 1.5 0 012 13.5z" fill="#fff"></path></svg></div><span style="font-size:10px; color:#b5b5bd;">Files</span></div>
<div style="display:flex; flex-direction:column; align-items:center; gap:6px; cursor:pointer;"><div style="width:40px; height:40px; border-radius:11px; background:linear-gradient(135deg,#ff8a7a,#ff5f57); display:flex; align-items:center; justify-content:center;"><svg width="18" height="18" viewBox="0 0 18 18"><path d="M9 2a5 5 0 00-5 5c0 4-1.5 5-1.5 5h13S14 11 14 7a5 5 0 00-5-5z" fill="#fff"></path><path d="M7.5 15a1.5 1.5 0 003 0" fill="#fff"></path></svg></div><span style="font-size:10px; color:#ff8a7a;">Routines</span></div>
<div style="display:flex; flex-direction:column; align-items:center; gap:6px; cursor:pointer;"><div style="width:40px; height:40px; border-radius:11px; background:linear-gradient(135deg,#ff8a7a,#ff5f57); display:flex; align-items:center; justify-content:center;"><svg width="18" height="18" viewBox="0 0 18 18"><circle cx="9" cy="9" r="2.6" fill="#fff"></circle><path d="M9 1.5v2M9 14.5v2M1.5 9h2M14.5 9h2M3.7 3.7l1.4 1.4M12.9 12.9l1.4 1.4M14.3 3.7l-1.4 1.4M5.1 12.9l-1.4 1.4" stroke="#fff" stroke-width="1.5"></path></svg></div><span style="font-size:10px; color:#b5b5bd;">Settings</span></div>
</div>
</div>
</sc-if>
</div>
<!-- STATUS BAR -->
<div style="height:28px; flex:none; display:flex; align-items:center; gap:18px; padding:0 16px; border-top:1px solid rgba(255,255,255,.06); background:#0a0a0c; font-family:'JetBrains Mono',monospace; font-size:10px; color:#5a5a62;">
<span style="color:#5fd08a;">● durable runner ok</span>
<span>checkpoint 3s ago</span>
<span style="flex:1;"></span>
<span>§15 sandbox: isolated</span>
<span style="color:{{ doorsValColor }};">{{ doorsStatusText }}</span>
</div>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props="{&quot;liveStream&quot;:{&quot;editor&quot;:&quot;boolean&quot;,&quot;default&quot;:true,&quot;tsType&quot;:&quot;boolean&quot;},&quot;showComputer&quot;:{&quot;editor&quot;:&quot;boolean&quot;,&quot;default&quot;:true,&quot;tsType&quot;:&quot;boolean&quot;},&quot;defaultPromptOpen&quot;:{&quot;editor&quot;:&quot;boolean&quot;,&quot;default&quot;:false,&quot;tsType&quot;:&quot;boolean&quot;}}">
class Component extends DCLogic {
state = { selected: 'morpheus', promptOpen: null, doorApproved: false };
renderVals() {
const s = this.state;
const live = this.props.liveStream ?? true;
const showComputer = this.props.showComputer ?? true;
const AGENTS = {
morpheus: {
name: 'Morpheus', role: 'Project Manager', spec: 'Rust 2024 Specialist',
initial: 'M', grad: 'linear-gradient(135deg,#ff8a7a,#ff5f57)', ink: '#2a0d0a',
model: 'Claude Sonnet 4.5', modelShort: 'claude-sonnet', status: 'working', statusColor: '#5ec8d8',
action: '▸ reviewing PR #214',
tok: '38.4k', spend: '$0.42', loops: 3, mem: '2,418', doors: 1,
commits: '9,105', hot: '11',
taskTitle: 'Review PR #214 — borrow-checker fix in', taskCode: 'cm-runtime', taskElapsed: 'task · 02:14 elapsed',
taskSteps: [
{ icon: '✓', iconColor: '#5fd08a', text: 'read cm-runtime/src/sandbox.rs', meta: '· 3 hunks' },
{ icon: '✓', iconColor: '#5fd08a', text: 'ran cargo clippy --workspace', meta: '· clean' },
{ icon: '▸', iconColor: '#5ec8d8', text: "checking lifetime on &'a mut Guard across await", meta: '', caret: true },
],
reasoningTime: '18:16:09',
reasoning: [
{ tag: 'think', tagColor: '#c98af0', text: 'The original holds the MutexGuard across .await — the std::sync::Mutex-across-await footgun my rules forbid.' },
{ tag: 'note', tagColor: '#9a9aa2', text: 'The fix clones under a scoped lock, dropping the guard before the await point. Sound.' },
{ tag: 'tool', tagColor: '#5ec8d8', text: 'github.review.comment → door check…' },
{ tag: 'door', tagColor: '#e8b465', text: 'post review on PR #214 needs approval', caret: true },
],
personality: ['Analytical', 'Concise', 'Skeptical', 'Direct'],
skillsCount: 7, skills: ['Web research', 'Summarize', 'Data viz', 'Code review'], skillsMore: '+3',
capabilities: ['File mgmt', 'Scheduling', 'Code exec'],
tools: [
{ name: 'Email', nameColor: '#cfcfd5', state: 'gated ✓', stateColor: '#5fd08a' },
{ name: 'Slack', nameColor: '#cfcfd5', state: 'gated ✓', stateColor: '#5fd08a' },
{ name: 'Browser', nameColor: '#cfcfd5', state: 'gated ✓', stateColor: '#5fd08a' },
{ name: 'Shell', nameColor: '#7a7a82', state: 'blocked ', stateColor: '#e8b465' },
],
memory: [
{ k: 'Long-term', v: '2,418 notes' },
{ k: 'Recent context', v: '18 msgs' },
{ k: 'Pinned facts', v: '4' },
],
safety: [
{ k: 'Sandbox', v: 'isolated', color: '#6fd0c0' },
{ k: 'Network', v: 'none', color: '#6fd0c0' },
{ k: 'Egress', v: 'gated', color: '#e8b465' },
],
doorTitle: 'Post code review on PR #214',
doorDetail: 'github.review.comment → external egress · drafts 1 comment',
running: [
{ name: 'Q3 competitor scan', dot: '#5ec8d8', blink: true, meta: 'loop · step 14', metaColor: '#5ec8d8', bar: '62%' },
{ name: 'Daily digest', dot: '#e8b465', blink: false, meta: 'cron · 08:00', metaColor: '#e8b465', bar: '' },
],
promptText: 'You are Morpheus, a senior engineering project manager for the Growth Team.\n\nYou review code with a focus on correctness, concurrency safety, and Rust 2024 idioms. You are concise and direct — flag the issue, propose the fix, cite the rule.\n\nNever hold a std::sync::Mutex guard across an .await point. Prefer scoped locks. Always run cargo clippy before approving.',
},
smith: {
name: 'Smith', role: 'Research Specialist', spec: 'Source Intelligence',
initial: 'S', grad: 'linear-gradient(135deg,#6fd0c0,#4aa3b8)', ink: '#06201f',
model: 'Claude Sonnet 4.5', modelShort: 'claude-sonnet', status: 'running', statusColor: '#5fd08a',
action: '▸ crawling 12 sources',
tok: '22.1k', spend: '$0.28', loops: 2, mem: '1,290', doors: 0,
commits: '4,602', hot: '6',
taskTitle: 'Crate audit — scan dependency tree in', taskCode: 'cargo audit', taskElapsed: 'task · 01:02 elapsed',
taskSteps: [
{ icon: '✓', iconColor: '#5fd08a', text: 'fetched advisory-db', meta: '· 184 crates' },
{ icon: '✓', iconColor: '#5fd08a', text: 'ran cargo audit --json', meta: '· 2 advisories' },
{ icon: '▸', iconColor: '#5ec8d8', text: 'summarizing RUSTSEC-2025-00xx (tokio)', meta: '', caret: true },
],
reasoningTime: '18:16:21',
reasoning: [
{ tag: 'search', tagColor: '#5ec8d8', text: 'crawling 12 sources for tokio CVE context and patched versions.' },
{ tag: 'note', tagColor: '#9a9aa2', text: 'Cross-referencing RUSTSEC + GHSA advisories; deduping by CVE id.' },
{ tag: 'tool', tagColor: '#5ec8d8', text: 'cargo.audit → running, no door required (read-only).' },
{ tag: 'result', tagColor: '#5fd08a', text: '2 advisories found · drafting summary for Morpheus', caret: true },
],
personality: ['Thorough', 'Curious', 'Literal'],
skillsCount: 6, skills: ['Web crawl', 'Summarize', 'Citations', 'Dedup'], skillsMore: '+2',
capabilities: ['Web fetch', 'Indexing', 'Export'],
tools: [
{ name: 'Browser', nameColor: '#cfcfd5', state: 'gated ✓', stateColor: '#5fd08a' },
{ name: 'Slack', nameColor: '#cfcfd5', state: 'gated ✓', stateColor: '#5fd08a' },
{ name: 'Email', nameColor: '#7a7a82', state: 'blocked ', stateColor: '#e8b465' },
{ name: 'Shell', nameColor: '#7a7a82', state: 'blocked ', stateColor: '#e8b465' },
],
memory: [
{ k: 'Long-term', v: '1,290 notes' },
{ k: 'Recent context', v: '11 msgs' },
{ k: 'Pinned facts', v: '2' },
],
safety: [
{ k: 'Sandbox', v: 'isolated', color: '#6fd0c0' },
{ k: 'Network', v: 'read-only', color: '#e8b465' },
{ k: 'Egress', v: 'none', color: '#6fd0c0' },
],
doorTitle: '', doorDetail: '',
running: [
{ name: 'Dependency watch', dot: '#5ec8d8', blink: true, meta: 'loop · step 7', metaColor: '#5ec8d8', bar: '48%' },
{ name: 'Nightly digest', dot: '#5fd08a', blink: false, meta: 'done · 02:00', metaColor: '#5fd08a', bar: '' },
],
promptText: 'You are Smith, a research specialist for the Growth Team.\n\nYou crawl, read, and synthesize sources into tightly-cited summaries. You never assert a fact without a source. You dedupe aggressively and prefer primary advisories (RUSTSEC, GHSA, NVD) over secondary reporting.\n\nYou are read-only by default — surface findings, do not act on them.',
},
};
const a = AGENTS[s.selected] || AGENTS.morpheus;
const order = ['morpheus', 'smith'];
const agents = order.map(id => {
const ag = AGENTS[id];
const sel = id === s.selected;
return {
id, name: ag.name, role: ag.role, initial: ag.initial, grad: ag.grad, ink: ag.ink,
action: ag.action, statusColor: ag.statusColor,
selected: sel,
rowBg: sel ? 'rgba(255,111,97,.1)' : 'transparent',
rowBorder: sel ? 'rgba(255,111,97,.25)' : 'transparent',
labelColor: sel ? '#ffffff' : '#eaeaee',
roleColor: sel ? '#ff8a7a' : '#6a6a72',
onSelect: () => this.setState({ selected: id, doorApproved: false }),
};
});
const defaultOpen = !!(this.props.defaultPromptOpen ?? false);
const promptOpen = s.promptOpen ?? defaultOpen;
const doorsLive = (a.doors > 0 && !s.doorApproved) ? a.doors : 0;
const showDoor = a.doors > 0 && !s.doorApproved;
const doorClear = a.doors === 0 || s.doorApproved;
const anim = (css) => live ? css : '';
return {
selName: a.name, selRole: a.role, selSpec: a.spec, selInitial: a.initial,
selGrad: a.grad, selInk: a.ink, selModel: a.model, selModelShort: a.modelShort,
selStatusLabel: a.status, selTok: a.tok, selSpend: a.spend, selLoops: a.loops, selMem: a.mem,
selCommits: a.commits, selHot: a.hot,
agents,
taskTitle: a.taskTitle, taskCode: a.taskCode, taskElapsed: a.taskElapsed, taskSteps: a.taskSteps,
reasoning: a.reasoning, reasoningTime: a.reasoningTime,
personality: a.personality,
skills: a.skills, skillsCount: a.skillsCount, skillsMore: a.skillsMore,
capabilities: a.capabilities,
tools: a.tools, memory: a.memory, safety: a.safety,
promptOpen, promptText: a.promptText,
promptChars: a.promptText.length.toLocaleString(),
promptChevron: promptOpen ? '▾' : '▸',
togglePrompt: () => this.setState({ promptOpen: !promptOpen }),
showDoor, doorClear, doorTitle: a.doorTitle, doorDetail: a.doorDetail,
approveDoor: () => this.setState({ doorApproved: true }),
doorsLive,
doorsBorder: doorsLive > 0 ? 'rgba(232,196,106,.3)' : 'rgba(255,255,255,.06)',
doorsLabelColor: doorsLive > 0 ? '#e8b465' : '#6a6a72',
doorsValColor: doorsLive > 0 ? '#e8b465' : '#5fd08a',
doorsStatusText: doorsLive > 0 ? (doorsLive + ' door awaiting approval') : 'no doors pending',
showComputer,
running: a.running.map(r => ({
name: r.name, meta: r.meta, metaColor: r.metaColor, dot: r.dot,
dotAnim: (live && r.blink) ? 'animation:cm-blink 1.4s infinite;' : '',
hasBar: !!r.bar, bar: r.bar,
})),
blinkAnim: anim('animation:cm-blink 1.4s infinite;'),
haloAnim: anim('animation:cm-halo 2s ease-out infinite;'),
caretAnim: anim('animation:cm-type 1s steps(1) infinite;'),
barAnim: anim('animation:cm-bar 1.6s ease-in-out infinite;'),
barAnim2: anim('animation:cm-bar 1.9s ease-in-out infinite;'),
};
}
}
</script>
</body>
</html>