feat(stt): Phase 3 — actions admin pilotées par le LLM (admin_action)

Asa peut désormais AGIR sur le homelab quand on le demande explicitement, via
un outil de la boucle agentique — mais jamais sans confirmation.

- Outil admin_action(description) (contexte asa) : le LLM PROPOSE une action,
  n'exécute rien. brain.ask_with_tools gagne `confirm_tools` : un tel outil
  arrête la boucle et surface sa réponse (la question de confirmation).
- _handle_agentic : stocke la proposition en pending par session ; au tour
  suivant « confirme » → agent.run_action → hermes-exec (hermes -z --yolo),
  « annule » → oubli. Réutilise le handshake + jeton du contexte agent.
- admin_action n'est exposé que si _actions_available() (STT_ACTIONS_ENABLED
  + jeton) ; sinon retiré des schémas envoyés au modèle.
- Factorisation du ctx_debug du visualiseur. 1 test unitaire (confirm_tools
  arrête la boucle). Serveur 0.9.0 ; doc stt.md + journal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
alkatrazz 2026-06-22 22:26:12 +02:00
parent 55344b55ed
commit 5543edce46
9 changed files with 163 additions and 28 deletions

View file

@ -146,6 +146,12 @@ async def _web_search(args: dict, deps: ToolDeps) -> str:
return "\n".join(lines)
async def _admin_action_placeholder(args: dict, deps: ToolDeps) -> str:
# Jamais atteint : app.py intercepte admin_action dans son dispatch (handshake de
# confirmation). Présent uniquement pour que le schéma soit exposé au LLM via le registre.
return "Action en attente de confirmation."
# --- Registre : nom → (schéma OpenAI, exécuteur) --------------------------------
_TOOLS: dict[str, tuple[dict, object]] = {
@ -243,6 +249,30 @@ _TOOLS: dict[str, tuple[dict, object]] = {
},
_web_search,
),
"admin_action": (
{
"type": "function",
"function": {
"name": "admin_action",
"description": "PROPOSE une action d'administration qui MODIFIE le homelab "
"(redémarrer un service/déploiement, relancer un pod, appliquer un changement…). "
"N'exécute RIEN directement : l'utilisateur devra confirmer vocalement. À utiliser "
"uniquement pour une demande explicite d'AGIR, jamais pour une simple lecture.",
"parameters": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "Action à effectuer, en une phrase claire et impérative "
"(ex. « redémarrer le déploiement open-webui »).",
}
},
"required": ["description"],
},
},
},
_admin_action_placeholder,
),
}