Aller au contenu
Tous les projets
Capture d’écran d’Agora : la liste des conversations et le panneau de modification de l’agent Signature
  • 2026
  • Agents IA
  • Messenger
  • Self-hosted
  • TypeScript

Agora est un messenger d’équipe auto-hébergé pour Hermes Agent, le runtime d’agents open source de Nous Research : chaque agent est un profil Hermes, et l’équipe lui parle en message privé ou en groupe. Je l’ai construit en TypeScript, de l’interface au déploiement, et le code est public sous licence AGPL-3.0.

Agora est une application de messagerie pour travailler en équipe avec des agents IA, installée sur son propre serveur. Elle s’appuie sur Hermes Agent, le runtime open source de Nous Research : chaque agent d’Agora est un profil Hermes, avec son modèle, sa personnalité, ses outils, ses skills et ses connecteurs MCP. Je l’ai construit pour qu’une organisation puisse utiliser Hermes sans passer par la ligne de commande, et pour gérer ce qui manque dès que plusieurs personnes partagent les mêmes agents : les accès, la mémoire commune, l’administration et les mises à jour.

Le problème

Hermes est organisé autour de profils : un dossier par agent, avec sa configuration, sa mémoire et ses sessions, plus une API HTTP, un tableau de bord interne et une CLI. Pour donner les mêmes agents à toute une équipe, il manque une couche au-dessus : décider qui a accès à quel agent, comment un agent sait à qui il parle, où vit ce que l’équipe a appris, et comment mettre Hermes à jour sans casser l’application qui en dépend.

Exemple concret : la mémoire intégrée de Hermes sur l’utilisateur (USER.md) est unique par profil. Si plusieurs personnes parlent au même agent, tout finit au même endroit. Agora envoie donc à chaque tour l’identité de l’interlocuteur et demande à l’agent de préfixer par le nom de la personne ce qu’il note sur elle.

Ce que fait Agora

C’est une messagerie : conversations directes et de groupe, entre humains et avec des agents, réponses en streaming avec l’activité des outils affichée en direct, pièces jointes, commandes « / » pour appeler un skill ou un connecteur. Quand un agent veut lancer une action sensible, Hermes met le tour en pause et la demande d’autorisation apparaît dans la conversation. Dans un groupe, un agent répond quand on le mentionne ou qu’on lui répond, et peut mentionner un autre agent pour lui passer la main ; ces relais sont plafonnés à douze par échange pour que deux agents ne se relancent pas indéfiniment.

Un admin crée les agents et règle leur modèle, leur personnalité (le fichier SOUL.md du profil), leurs outils, skills et serveurs MCP. Un nouveau bot peut aussi se configurer en discutant : il pose ses questions puis écrit lui-même son SOUL.md. L’accès se fait sur invitation, avec des droits par personne sur chaque agent et une double authentification obligatoire depuis la v0.4.0. Au premier lancement, un assistant configure l’organisation, le compte admin, le fournisseur d’IA (clé testée en direct) et le premier agent, la création du compte admin étant protégée par un code affiché dans les logs du serveur.

Architecture

Agora est un monorepo TypeScript géré avec pnpm et Turborepo :

apps/api
le serveur : Bun, Hono, Drizzle ORM sur Postgres, Better Auth.
apps/web
le client : React 19, TanStack Router et Query, Tailwind v4, shadcn/ui.
apps/updater
le service de mises à jour de l’app et de Hermes.
packages/core
le code partagé : types d’événements, traductions.
apps/mobile
une app iOS en React Native avec Expo, sur le même paquet core.

Pour produire les réponses, l’API passe par Hermes, en mode multi-profils. Chaque profil est joignable sous /p/<profil> avec sa propre clé, dérivée par HMAC d’un secret commun, ce qui évite de stocker une clé par agent. Le dépôt contient aussi trois plugins Hermes en Python : agora_wiki pour la mémoire partagée, agora_files pour lire les pièces jointes en lecture seule, agora_screen pour voir en direct le navigateur qu’un agent pilote. Seule exception à Hermes : le propriétaire d’un abonnement Claude ou ChatGPT connecté sur la machine peut faire répondre ses agents par Claude Code ou Codex, dans les tours qu’il lance lui-même.

Du message à la réponse en streaming

Chaque message crée un tour par agent concerné, placé dans une file propre au couple conversation-agent : un agent ne traite jamais deux tours à la fois dans la même session Hermes, mais des agents différents répondent en parallèle. Les tours sont gardés en base : au redémarrage de l’API, ceux en attente sont relancés et ceux en cours sont signalés comme interrompus plutôt que renvoyés, parce que Hermes a peut-être déjà exécuté des outils.

L’API lit le flux SSE du run Hermes (texte, outils, demandes d’autorisation) et republie chaque événement sur un bus en mémoire, vers un flux SSE unique par onglet (GET /api/events). Les envois passent par des POST classiques : la spec de conception a écarté WebSocket. C’est aussi Agora qui nomme les sessions Hermes. Comme Hermes garde en cache le prompt système de chaque session, un changement de personnalité incrémente une révision qui force une nouvelle session, et /new ou /compact passent à une nouvelle génération :

apps/api/src/company.ts
export const hermesSessionId = (conversationId: string, revision: number, groupAgentId?: string, generation = 0) => {
  const base = groupAgentId ? `agora-${conversationId}-${groupAgentId}` : `agora-${conversationId}`;
  return `${base}${revision > 0 ? `-r${revision}` : ""}${generation > 0 ? `-g${generation}` : ""}`;
};

Une mémoire commune à tous les agents

La mémoire partagée a deux niveaux : une mémoire courte de l’organisation (20 000 caractères au plus, éditée par un admin, envoyée à chaque tour) et un wiki markdown commun à tous les profils, sur le modèle du « LLM Wiki » d’Andrej Karpathy, lisible dans l’app ou dans Obsidian. Le plugin agora_wiki se branche comme fournisseur de mémoire de Hermes : il donne aux agents des outils wiki_*, injecte un fichier hot.md de contexte chaud dans chaque session et journalise chaque tour dans raw/, sous verrou de fichier puisque plusieurs profils écrivent en parallèle. Un curateur côté API compile ensuite ce matériau brut en pages durables, vérifie que chaque source annoncée est bien citée, et laisse à la relecture humaine celles qui échouent trois fois.

Choix techniques et difficultés

Une bonne partie du travail a porté sur ce que les agents peuvent faire sur le serveur : quiconque écrit à un agent pilote ses outils, y compris via du texte caché dans une page web qu’il lit. Or certains outils de Hermes (terminal, exécution de code, fichiers, navigateur local, contrôle de l’ordinateur) donnent la main sur la machine : un agent pourrait réécrire la configuration d’un autre profil. Chaque profil démarre donc sans eux, et un admin peut les réactiver agent par agent après un avertissement. Pour la même raison, les images externes dans les réponses ne se chargent qu’au clic.

Agora dépend aussi d’un logiciel qui évolue vite : depuis Hermes v2026.9.14, la route /v1/skills est cassée et Agora lit les skills via le tableau de bord ; les versions 9.x n’activent plus seules le mode multi-profils, qu’Agora déclare maintenant au démarrage. J’ai donc écrit un contrat : un script qui vérifie les routes, les formats et les flux SSE dont l’API dépend, jusqu’à une vraie réponse d’agent.

Côté client, les réponses rapides mettaient à jour le fil à chaque token, et le surligneur de code de Streamdown déclenchait un setState dans le même commit : au-delà de cinquante mises à jour imbriquées, React s’arrêtait sur son erreur #185. La correction, en v0.8.1, affiche le premier token tout de suite puis regroupe la suite en au plus une mise à jour toutes les 50 ms.

Déploiement et mises à jour

Agora s’installe avec Docker Compose sur un serveur Linux : Caddy (HTTPS automatique et app web), l’API, Hermes, Postgres et le service de mises à jour. Hermes n’écoute que sur 127.0.0.1, Postgres est sur un réseau interne, et seul Caddy publie des ports. Un script agora regroupe les opérations courantes, jusqu’à la sauvegarde hors site chiffrée avec restic.

cd agora/infra
cp .env.example .env   # DOMAIN et secrets (openssl rand -hex 32)
./agora build
./agora up             # HTTPS via Caddy
./agora setup-code     # code demandé pour créer le compte admin

Chaque push sur main qui passe la CI devient une release versionnée à partir des commits conventionnels, avec images publiées sur ghcr.io. L’updater vérifie toutes les heures et applique la nuit, par défaut entre 3 h et 5 h. Pour l’app, patchs et mineures s’installent seuls, les majeures sur un clic : sauvegarde, bascule, migrations au démarrage de l’API, vérification du contrat, sinon retour arrière avec restauration de la base.

Pour Hermes, c’est plus prudent. L’updater joue le contrat sur la production pour avoir une référence, sauvegarde, puis démarre un Hermes « canari » sur une copie des données, sans tâches planifiées ni tokens de messagerie pour qu’il ne déclenche rien en double. Seule une régression par rapport à la référence bloque. Si tout passe, la production bascule et le contrat est rejoué ; sinon, retour à la version précédente avec restauration des données, et la version refusée n’est plus retentée automatiquement.

Où en est le projet

Agora est en v0.10.0, sous licence AGPL-3.0. L’historique public commence par un commit qui contient déjà l’essentiel de l’application ; les versions suivantes ont durci la sécurité (réseau interne pour Postgres, logs Caddy pour CrowdSec, SSH via Tailscale), corrigé le streaming et l’accès des agents aux connecteurs MCP, puis ajouté des graphiques dans les réponses, plusieurs comptes Claude Code, un moteur Codex et des formulaires de connecteurs adaptés à chaque champ.

Il reste des limites connues. Le bus d’événements est en mémoire, donc l’API tourne en une seule instance ; le code note qu’il faudrait passer par LISTEN/NOTIFY de Postgres pour en lancer plusieurs. Après une coupure réseau, le client recharge ses données sans que le serveur rejoue les événements manqués.