// Page 3 — Team function TeamPage({ alerts, pushToast }) { const [expanded, setExpanded] = useState(() => new Set(["kostya"])); const [confirmRestart, setConfirmRestart] = useState(null); const alertsByAgent = useMemo(() => { const m = {}; alerts.forEach(a => { if (a.status === "active" && a.agent) (m[a.agent] = m[a.agent] || []).push(a); }); return m; }, [alerts]); const toggle = (id) => { setExpanded((prev) => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; }); }; return (

Команда

{TEAM.length} агентов · {TEAM.filter(t => t.watcherOnline).length} watcher'ов онлайн
0 ? "1fr" : "1fr 1fr", gap: 16 }}> {/* Render rows; expanded ones go full width */} setConfirmRestart(a)} />
setConfirmRestart(null)} onConfirm={() => { pushToast(`Watcher ${confirmRestart.name} перезапускается…`, "warn"); setConfirmRestart(null); }} />
); } function TeamGrid({ team, expanded, toggle, alertsByAgent, onRestart }) { return (
{team.map((a) => ( expanded.has(a.id) ? ( toggle(a.id)} onRestart={() => onRestart(a)} /> ) : ( toggle(a.id)} /> ) ))}
); } function AgentRow({ agent, alerts, onExpand }) { const health = agent.health; const bar = health === "crit" ? "var(--crit)" : health === "warn" ? "var(--warn)" : "var(--ok)"; return (
{agent.name}
{agent.roleLabel}
{agent.currentTask ? (
PB-{agent.currentTask.seq} {agent.currentTask.title}
) : (
— свободен —
)}
{agent.modulesCurrent.map(m => )}
context
= 70 ? "var(--ok)" : agent.contextPct >= 30 ? "var(--warn)" : "var(--crit)" }}>{agent.contextPct}%
watcher
{agent.watcherOnline ? {Math.floor(agent.watcherUptimeMin/60)}ч {agent.watcherUptimeMin%60}м : offline}
{alerts.length > 0 && ⚠ {alerts.length} алерт{alerts.length === 1 ? "" : "а"}}
); } function AgentBigCard({ agent, alerts, onCollapse, onRestart }) { const health = agent.health; const accent = health === "crit" ? "var(--crit)" : health === "warn" ? "var(--warn)" : "var(--ok)"; // Mock comments const comments = [ { ts: "2026-05-17T14:28:04Z", text: "progress: правлю Footer.tsx, осталось header" }, { ts: "2026-05-17T14:18:30Z", text: "взял задачу, начинаю с поиска вхождений Print-X" }, { ts: "2026-05-17T13:45:11Z", text: "вернулся к PB-47, всё ещё тыкаю интеграцию FastAPI" }, ]; return (
{/* Header bar with health stripe */}
{agent.name}
{agent.roleLabel} {agent.email}
watcher {agent.watcherOnline ? "online" : "offline"} {agent.watcherOnline && ( <> PID: {agent.watcherPid} uptime: {Math.floor(agent.watcherUptimeMin/60)}ч {agent.watcherUptimeMin%60}м heartbeat: {agent.lastHeartbeatMin.toFixed(1)}с назад )} {!agent.watcherOnline && ( последний heartbeat: {Math.round(agent.lastHeartbeatMin)} мин назад )}
{/* Body — 3 columns */}
{/* Column 1 — current task & comments */}
Сейчас занимается
{agent.currentTask ? ( <>
PB-{agent.currentTask.seq} {agent.currentTask.title}
Последние комментарии в Plane
{comments.slice(0, 3).map((c, i) => (
{fmtTime(c.ts)}
{c.text}
))}
) : (
— свободен — ждёт следующей задачи
)} {alerts.length > 0 && (
Активные алерты
{alerts.map(al => (
{al.text}
))}
)}
{/* Column 2 — context + tool calls */}
Контекст
За день
= 70 ? "var(--ok)" : agent.contextPct >= 30 ? "var(--warn)" : "var(--crit)"} />
с {agent.contextHistory[0]}% до {agent.contextHistory[agent.contextHistory.length - 1]}%
Среднее tool-calls на задачу
{agent.avgToolCalls}
{agent.avgToolCalls < 15 ? "лёгкие задачи" : agent.avgToolCalls < 25 ? "средняя сложность" : "тяжёлые задачи / debugging"}
Нагрузка сегодня
{/* Column 3 — expertise */}
Сейчас в работе
{agent.modulesCurrent.map(m => )}
Историческая экспертиза
{agent.modulesHistorical.map((h) => (
{h.lastSeen}
))}
); } window.TeamPage = TeamPage;