"""Agent soul profiles and Herman permissions.""" from __future__ import annotations from typing import Any, Optional from app.db import execute, fetch_all, fetch_one def list_souls() -> list[dict[str, Any]]: rows = fetch_all( """SELECT s.*, (SELECT COUNT(*) FROM agent_events e WHERE LOWER(e.agent_name) = s.agent_key) AS event_count, (SELECT MAX(created_at) FROM agent_events e WHERE LOWER(e.agent_name) = s.agent_key) AS last_event_at, (SELECT title FROM agent_events e WHERE LOWER(e.agent_name) = s.agent_key ORDER BY created_at DESC LIMIT 1) AS current_task, (SELECT status FROM agent_events e WHERE LOWER(e.agent_name) = s.agent_key ORDER BY created_at DESC LIMIT 1) AS current_status, (SELECT event_type FROM agent_events e WHERE LOWER(e.agent_name) = s.agent_key ORDER BY created_at DESC LIMIT 1) AS current_event_type FROM agent_souls s ORDER BY s.display_name""" ) return [dict(r) for r in rows] def get_soul(agent_key: str) -> Optional[dict[str, Any]]: row = fetch_one( """SELECT s.*, (SELECT COUNT(*) FROM agent_events e WHERE LOWER(e.agent_name) = s.agent_key) AS event_count FROM agent_souls s WHERE agent_key = %s""", (agent_key.lower(),), ) if not row: return None out = dict(row) out["recent_events"] = list_agent_events(agent_key, limit=15) return out def list_agent_events(agent_key: str, limit: int = 50) -> list[dict[str, Any]]: rows = fetch_all( """SELECT id, agent_name, event_type, title, body, status, channel, metadata, created_at, completed_at FROM agent_events WHERE LOWER(agent_name) = %s ORDER BY created_at DESC LIMIT %s""", (agent_key.lower(), limit), ) out: list[dict[str, Any]] = [] for row in rows: ev = dict(row) for key in ("created_at", "completed_at"): if ev.get(key) is not None and hasattr(ev[key], "isoformat"): ev[key] = ev[key].isoformat() out.append(ev) return out def update_soul(agent_key: str, **fields: Any) -> dict[str, Any]: allowed = ("display_name", "role_title", "soul_md", "responsibilities", "permissions", "is_active") sets, params = [], [] for k, v in fields.items(): if k in allowed and v is not None: sets.append(f"{k} = %s") params.append(v) if not sets: soul = get_soul(agent_key) if not soul: raise ValueError("Agent not found") return soul params.append(agent_key.lower()) execute(f"UPDATE agent_souls SET {', '.join(sets)}, updated_at = NOW() WHERE agent_key = %s", tuple(params)) return get_soul(agent_key) or {} def list_permissions() -> list[dict[str, Any]]: return [dict(r) for r in fetch_all("SELECT * FROM herman_permissions ORDER BY category, module_label")] def update_permission(module_key: str, granted: bool) -> dict[str, Any]: execute( """UPDATE herman_permissions SET granted = %s, granted_at = CASE WHEN %s THEN NOW() ELSE NULL END, updated_at = NOW() WHERE module_key = %s""", (granted, granted, module_key), ) row = fetch_one("SELECT * FROM herman_permissions WHERE module_key = %s", (module_key,)) return dict(row or {}) def grant_all_permissions() -> int: execute("UPDATE herman_permissions SET granted = TRUE, granted_at = NOW(), updated_at = NOW()") row = fetch_one("SELECT COUNT(*) AS n FROM herman_permissions WHERE granted = TRUE") return int((row or {}).get("n") or 0)