From 4e40dbf2c1a44ae88973fab2d9e65cc70c0ae8af Mon Sep 17 00:00:00 2001 From: bolbol Date: Thu, 23 Jul 2026 07:41:58 +0000 Subject: [PATCH] docs(mail-browser): garde-fous /draft-reply (confirmed_by_user, rate-limit 5/h, audit log) testes 2026-07-23 + reponse skills-en-cron --- common/MAIL-BROWSER-HERMES-TT.md | 57 ++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/common/MAIL-BROWSER-HERMES-TT.md b/common/MAIL-BROWSER-HERMES-TT.md index 6277fef..0081423 100644 --- a/common/MAIL-BROWSER-HERMES-TT.md +++ b/common/MAIL-BROWSER-HERMES-TT.md @@ -63,3 +63,60 @@ rm -f /volume1/docker/hermes-mail-browser/data/profile/SingletonLock \ Incident deja rencontre et resolu deux fois — cause = verrous Singleton perimes empechant Edge de redemarrer dans le profil persistant. + +--- + +## Garde-fous /draft-reply (phase 2.1 — 2026-07-23) + +Objectif : POST /draft-reply ne doit JAMAIS partir d'une initiative autonome +de hermes-tt (cron ou exces de zele), uniquement sur demande explicite de +Nabil dans le message en cours. Implemente dans `app/main.py`. + +- **confirmed_by_user (obligatoire)** : le modele DraftReply exige + `confirmed_by_user: bool`. Absent ou `false` -> HTTP 400 (aucun envoi). + L'agent ne met `true` que si Nabil a explicitement demande la reponse. +- **Rate-limit (circuit-breaker)** : 5 envois reussis max par fenetre + glissante d'une heure. Au-dela -> HTTP 429. La fenetre est calculee depuis + le journal d'audit (source de verite persistante, survit aux redemarrages). +- **Journal d'audit persistant** : `/data/draft_reply_audit.log` (= + `/volume1/docker/hermes-mail-browser/data/draft_reply_audit.log`), 1 ligne + JSON par appel (accepte ou rejete) : timestamp ISO, subject, body_len, + confirmed_by_user recu, result (sent / rejected_no_confirmation / + rejected_rate_limit / error_*), original_index. +- **GET /draft-reply/audit?limit=N** : lecture seule des N derniers appels. + +### Tests reussis (2026-07-23) +- POST sans confirmed_by_user -> **400** (message garde-fou), aucun envoi. OK +- POST confirmed_by_user=true -> **200**, mail projet recu dans la boite pro. OK +- 6 requetes confirmed_by_user=true en rafale -> req #1-5 **200**, req #6 + **429** (rate-limit). OK +- GET /draft-reply/audit -> 7 entrees correctes (1 rejet-confirmation, 5 + sent, 1 rejet-rate-limit). OK +- Journal de test archive en `draft_reply_audit.log.selftest-20260723` et + journal production remis a zero (budget rate-limit plein pour l'usage reel). + +### Deploiement (methode prudente, sans recreate) +- `main.py` modifie sur l'hote, `ast.parse` OK, sauvegarde `main.py.bak-*`. +- `/app` n'est PAS bind-monte (seul `/data` l'est) -> `docker cp main.py` + dans le conteneur EN COURS, puis `docker restart hermes-mail-browser`. +- Le restart est sur : `start.sh` supprime deja les verrous + Singleton{Lock,Cookie,Socket} + /tmp/.X99-lock au demarrage (idempotent), + et Edge repart sur le profil persistant -> session O365 conservee + (`/auth/status` -> authenticated:true apres restart, verifie). + +## Skills en contexte cron (verifie 2026-07-23, hermes_agent 0.17.0) + +Question : un job cron avec `"skills": []` charge-t-il tous les skills de +`/opt/data/skills/` ou aucun ? +**Reponse : aucun skill n'est PRECHARGE dans le prompt du job.** +`cron/scheduler.py::_build_job_prompt` : quand la liste `skills` est vide, +`skill_names` est vide -> le prompt est assemble avec `has_skills=False`, +AUCUN contenu de skill n'est injecte. Le champ `skills` ne prend effet que +s'il liste explicitement des noms de skills a charger. +**Nuance importante** : l'agent cron tourne quand meme avec le TOOLSET +COMPLET par defaut (`_resolve_cron_enabled_toolsets` -> full set ; seuls +`cronjob`, `messaging`, `clarify` + `{moa,homeassistant,rl}` sont retires). +Le toolset `skills` reste disponible -> un agent cron pourrait, de sa propre +initiative, charger `mail-o365-tt` et joindre l'API. D'ou l'interet des +garde-fous techniques cote hermes-mail-browser (le skill seul ne suffit pas a +garantir qu'aucun appel autonome ne parte).