Files
nas-runbooks/common/MAIL-BROWSER-HERMES-TT.md
T

6.2 KiB

MAIL-BROWSER-HERMES-TT

Runbook du service de lecture/reponse mail O365 pour hermes-tt, via un navigateur Edge deja authentifie tournant dans le conteneur hermes-mail-browser (session MFA maintenue cote serveur).

Architecture

  • Conteneur : hermes-mail-browser (reseau Docker n8n)
  • API interne : http://hermes-mail-browser:8000 (joignable par hermes-agent-tt sur le meme reseau, pas besoin du port 3110 expose)
  • noVNC : port 8810 (LAN uniquement) — pour reconnexion manuelle O365/MFA
  • Envoi des projets de reponse : SMTP ik.me vers la boite pro de Nabil (nabil.derouiche@tunisietelecom.tn)

Statut — Phase 2 : COMPLETE (deployee et testee avec succes)

Endpoints construits, deployes et testes le 2026-07-23 :

  • GET /health — conteneur sain, authenticated: true
  • GET /inbox?limit=N — liste (expediteur / objet / heure / apercu) ; aria_label contient "pieces jointes" si le mail en a
  • GET /message/{index} — corps texte complet du mail
  • GET /message/{index}/attachments — teste : liste des PJ, OK
  • GET /message/{index}/attachments/{filename}/text — teste sur .docx ET .pdf, extraction correcte confirmee (extraction auto PDF/DOCX/XLSX)
  • POST /draft-reply {"subject":"...","body_text":"..."} — teste : mail reellement recu dans la boite O365 pro. Envoie un PROJET vers la boite de Nabil (jamais vers le correspondant). Prefixe de sujet [REPONSE PROPOSEE] Re: <objet> ajoute automatiquement par l'API.
  • Reseau : hermes-agent-tt joint http://hermes-mail-browser:8000/health sans probleme (meme reseau Docker n8n) — teste, OK.

Skill hermes-tt

/opt/data/skills/mail-o365-tt.md (dans hermes-agent-tt) reecrit le 2026-07-23 : les anciennes sections "ENVOI MAIL — SMTP Infomaniak" et "LECTURE MAIL — OWA Navigateur" (methode browser_snapshot, obsolete car bloquee par le MFA) ont ete entierement remplacees par des instructions pointant vers l'API ci-dessus. Regle centrale conservee : hermes-tt ne repond JAMAIS directement a un correspondant, il envoie toujours un projet a Nabil qui decide et envoie lui-meme depuis OWA. Les Cas d'usage 1 (Trouver+envoyer), 3 (Ingestion AO), 4 (Veille reglementaire) et le bloc REGLES COMPORTEMENTALES ont ete conserves, adaptes a la nouvelle API.

Session expiree (code 409)

Si l'API renvoie 409, la session du navigateur a expire. NE JAMAIS tenter de re-authentifier depuis l'agent : signaler a Nabil qu'une reconnexion manuelle via noVNC (port 8810, LAN) est necessaire.

PIEGE — recreate / docker compose up -d du conteneur

Edge tue brutalement laisse des verrous perimes dans le volume persistant. AVANT tout docker compose up -d / recreate de hermes-mail-browser, TOUJOURS supprimer d'abord :

rm -f /volume1/docker/hermes-mail-browser/data/profile/SingletonLock \
      /volume1/docker/hermes-mail-browser/data/profile/SingletonCookie \
      /volume1/docker/hermes-mail-browser/data/profile/SingletonSocket

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).