docs: add TASK_PHASE_AGENT_PRESENCE_INDICATORS_MVP.md

Task for implementing real-time presence indicators for all agents.
Shows Matrix presence (online/unavailable/offline) and DAGI router health.
Integrates into agent lists, cabinets, node dashboards, microdao dashboards.
This commit is contained in:
Apple
2025-11-30 09:22:29 -08:00
parent e246e6d413
commit 111d62a62a

View File

@@ -0,0 +1,286 @@
# TASK PHASE — AGENT PRESENCE INDICATORS (MVP)
Version: 1.0
Status: ACTIVE
Target: DAARION.space (Next.js frontend + city-service)
## 1. Мета
Реалізувати **Presence Indicators** для всіх агентів у МВП:
- Online/Offline статус з Matrix presence
- DAGI Router heartbeat індикатори
- Візуальні бейджі у всіх місцях де відображаються агенти
Правило онтології:
> Агенти мають бути **"живими"** — їх статус має бути видимим завжди
---
## 2. Область робіт
### 2.1. Сторінки де потрібні Presence Indicators
1. **Agent List** (`/agents`) — кожен агент у списку
2. **Agent Cabinet** (`/agents/:agentId`) — основний статус агента
3. **Node Dashboard** (`/nodes/:nodeId`) — Node Core Agents (Guardian/Steward)
4. **MicroDAO Dashboard** (`/microdao/:slug`) — Orchestrator + team agents
5. **City Dashboard** (`/city`) — civic agents (DAARWIZZ, DARIO, DARIA)
### 2.2. Типи Presence
#### Matrix Presence
- `online` — користувач активний (зелений)
- `unavailable` — користувач неактивний >5 хв (жовтий)
- `offline` — користувач офлайн (сірий)
#### DAGI Router Presence
- `healthy` — DAGI router відповідає (зелений)
- `degraded` — проблеми з router (жовтий)
- `offline` — router недоступний (червоний)
---
## 3. Backend Implementation
### 3.1. Додати Presence API
Додати до `services/city-service/routes_city.py`:
```python
@api_router.get("/agents/presence")
async def get_agents_presence():
"""
Отримати presence статус всіх активних агентів.
"""
try:
# Get agents from DB
agents = await repo_city.list_agents_summaries(limit=1000)
# Get Matrix presence from matrix-presence-aggregator
matrix_presence = await get_matrix_presence_status()
# Get DAGI router health
dagi_health = await get_dagi_router_health()
# Combine data
presence_data = []
for agent in agents:
matrix_status = matrix_presence.get(agent["id"], "offline")
dagi_status = dagi_health.get(agent["node_id"], {}).get("router_status", "unknown")
presence_data.append({
"agent_id": agent["id"],
"matrix_presence": matrix_status,
"dagi_router_presence": dagi_status,
"last_seen": matrix_presence.get(f"{agent['id']}_last_seen"),
"node_id": agent.get("node_id")
})
return {"presence": presence_data}
except Exception as e:
logger.error(f"Failed to get agents presence: {e}")
raise HTTPException(status_code=500, detail="Failed to get agents presence")
```
### 3.2. Інтегрувати з існуючими Presence системами
Використати:
- `matrix-presence-aggregator` для Matrix presence
- `city-service/presence_gateway.py` для forwarding
- `usePresenceHeartbeat` hook для client-side heartbeats
---
## 4. Frontend Implementation
### 4.1. Presence Hook
Створити `apps/web/src/hooks/useAgentPresence.ts`:
```typescript
interface AgentPresence {
agent_id: string;
matrix_presence: 'online' | 'unavailable' | 'offline';
dagi_router_presence: 'healthy' | 'degraded' | 'offline' | 'unknown';
last_seen?: string;
node_id?: string;
}
export function useAgentPresence(agentIds?: string[]) {
const [presenceData, setPresenceData] = useState<Record<string, AgentPresence>>({});
// Fetch presence data
const fetchPresence = useCallback(async () => {
try {
const params = agentIds ? `?agent_ids=${agentIds.join(',')}` : '';
const res = await fetch(`/api/v1/agents/presence${params}`);
const data = await res.json();
setPresenceData(data.presence);
} catch (error) {
console.error('Failed to fetch agent presence:', error);
}
}, [agentIds]);
// Auto-refresh every 30 seconds
useEffect(() => {
fetchPresence();
const interval = setInterval(fetchPresence, 30000);
return () => clearInterval(interval);
}, [fetchPresence]);
return presenceData;
}
```
### 4.2. Presence Badge Component
Створити `apps/web/src/components/ui/AgentPresenceBadge.tsx`:
```typescript
interface AgentPresenceBadgeProps {
agentId: string;
size?: 'sm' | 'md' | 'lg';
showLabel?: boolean;
className?: string;
}
export function AgentPresenceBadge({
agentId,
size = 'sm',
showLabel = false,
className
}: AgentPresenceBadgeProps) {
const presenceData = useAgentPresence([agentId]);
const presence = presenceData[agentId];
if (!presence) return null;
const getStatusInfo = () => {
const matrixStatus = presence.matrix_presence;
const dagiStatus = presence.dagi_router_presence;
// Priority: Matrix status, then DAGI status
if (matrixStatus === 'online') {
return { color: 'bg-emerald-500', label: 'Online' };
} else if (matrixStatus === 'unavailable') {
return { color: 'bg-amber-500', label: 'Away' };
} else if (dagiStatus === 'healthy') {
return { color: 'bg-blue-500', label: 'Healthy' };
} else if (dagiStatus === 'degraded') {
return { color: 'bg-orange-500', label: 'Degraded' };
} else if (dagiStatus === 'offline') {
return { color: 'bg-red-500', label: 'Offline' };
} else {
return { color: 'bg-gray-500', label: 'Unknown' };
}
};
const statusInfo = getStatusInfo();
const sizeClasses = {
sm: 'w-2 h-2',
md: 'w-3 h-3',
lg: 'w-4 h-4'
};
return (
<div className={cn('flex items-center gap-1.5', className)}>
<div className={cn(
'rounded-full border border-white/20',
sizeClasses[size],
statusInfo.color
)} />
{showLabel && (
<span className="text-xs text-white/70">{statusInfo.label}</span>
)}
</div>
);
}
```
### 4.3. Інтеграція в Agent Cards
Додати Presence Badge до `apps/web/src/components/agent/AgentCard.tsx`:
```typescript
// Inside AgentCard component
<AgentPresenceBadge
agentId={agent.id}
size="sm"
showLabel={false}
className="absolute top-2 right-2"
/>
```
---
## 5. Acceptance Criteria
1. **Agent List** (`/agents`)
- Кожен агент має presence indicator у правому верхньому куті аватара
- Зеленый = online, Жовтий = away, Сірий = offline
- Індикатор оновлюється в реальному часі
2. **Agent Cabinet** (`/agents/:agentId`)
- Великий presence badge у заголовку
- Показує обидва статуси: Matrix + DAGI Router
- Індикатор "Last seen: 5 min ago"
3. **Node Dashboard** (`/nodes/:nodeId`)
- Guardian/Steward агенти мають presence indicators
- Показується DAGI router статус ноди
4. **MicroDAO Dashboard** (`/microdao/:slug`)
- Orchestrator агент має presence indicator
- Team agents мають presence indicators
5. **Real-time Updates**
- Presence оновлюється автоматично кожні 30 секунд
- Не блокує рендеринг якщо API недоступне
---
## 6. Файли до створення
### Backend
- `services/city-service/routes_presence.py` — presence API endpoints
- Інтеграція з `matrix-presence-aggregator`
- Інтеграція з DAGI router health checks
### Frontend
- `apps/web/src/hooks/useAgentPresence.ts` — presence hook
- `apps/web/src/components/ui/AgentPresenceBadge.tsx` — presence badge component
- Інтеграція в усі agent-related компоненти
### Конфігурація
- Додати presence API до Next.js rewrites
- Оновити presence gateway для forwarding agent presence
---
## 7. Тестування
```bash
# Перевірити API
curl https://daarion.space/api/v1/agents/presence
# Перевірити UI
# Відкрити /agents - подивитися на presence indicators
# Відкрити /agents/daarwizz - подивитися на великий badge
# Відкрити /nodes/node-1-hetzner-gex44 - presence для Guardian
```
---
## 8. Подальше розширення (не в цьому MVP)
- Agent typing indicators у кімнатах
- Agent "working on task" статус
- Presence history & analytics
- Push notifications для presence changes
- Agent availability scheduling
---
**Target Date**: Today
**Priority**: High (core UX improvement)
**Dependencies**: Matrix presence aggregator, DAGI router health API