Dans les coulisses : comment la résolution de session JSONL alimente le routage d'agents
Une plongée technique dans la façon dont MadoHub lit les journaux de session de Claude Code pour extraire des réponses propres et structurées pour le routage intelligent.
Le problème le plus difficile du routage d'agents n'est pas de générer le prompt pour l'agent cible. C'est d'extraire une réponse propre depuis l'agent source.
La sortie terminal est sale. Les codes d'échappement ANSI, les spinners de progression, les retours à la ligne, les séquences de couleur — le scraping brut du terminal vous donne un mur de bruit. Vous pouvez supprimer les codes de contrôle, mais vous vous retrouvez quand même avec des artefacts de formatage, des lignes partielles et du contenu mélangé.
MadoHub résout cela autrement. Au lieu de scrapper la sortie terminal, nous lisons les journaux de session structurés de Claude Code — les fichiers JSONL qui contiennent des représentations propres et sémantiques de chaque tour de conversation.
Cet article explique comment cela fonctionne.
Le problème du scraping terminal
Quand Claude Code s'exécute dans un terminal, sa sortie inclut :
- Des séquences d'échappement ANSI pour les couleurs, le positionnement du curseur et la mise en forme.
- Des marqueurs Unicode comme ❯ (invite), ✽ (réflexion), ⏺ (utilisation d'un outil).
- Des indicateurs de progression qui écrasent les lignes précédentes.
- Des boîtes de dialogue d'autorisation avec éléments interactifs.
- Des blocs de code sur plusieurs lignes avec des codes de surlignage syntaxique.
Supprimer les codes ANSI vous rapproche d'un texte propre, mais pas complètement. Les indicateurs de progression laissent des lignes partielles. Les blocs de code perdent leur structure. La frontière entre "sortie de l'agent" et "bruit du shell" devient ambiguë.
Pour un routage simple (par exemple "envoie les 10 dernières lignes"), le scraping terminal suffit. Mais pour le routage intelligent — où un modèle IA doit comprendre ce que l'agent a réellement fait et générer un prompt contextuel — il faut des données propres et structurées.
Comment Claude Code stocke les sessions
Claude Code écrit des journaux de session structurés au format JSONL (un objet JSON par ligne). Le chemin de résolution est :
Pane tmux → arborescence de processus via ps (BFS)
→ PID de l'agent (premier descendant dont la commande correspond à claude/codex/amp/opencode)
→ ~/.claude/sessions/{PID}.json
→ champ sessionId
→ ~/.claude/projects/*/{sessionId}.jsonl
→ tours de conversation (messages user/assistant)Chaque tour de conversation contient le rôle du message, son contenu et ses métadonnées, sans aucune mise en forme terminal. C'est la même donnée que Claude Code utilise en interne pour conserver le contexte de conversation.
La chaîne de résolution
MadoHub résout le fichier JSONL via une chaîne à plusieurs étapes :
Étape 1 : obtenir l'ID du processus PTY
Chaque tile de terminal repose sur un pane tmux, et MadoHub suit le PID de ce pane dès la création du terminal. Ce PID est le point de départ pour localiser le véritable processus de l'agent, qui s'exécute généralement comme descendant du shell du pane.
Étape 2 : trouver le PID de l'agent et son fichier de session
Claude Code maintient un répertoire de sessions dans ~/.claude/sessions/, mais chaque fichier porte le nom de l'ID de processus de l'agent — pas un ID de session. Pour trouver ce PID, MadoHub parcourt l'arborescence des processus depuis le pane tmux (une recherche en largeur avec ps -eo pid=,ppid=,comm= sur les descendants) jusqu'à atteindre le premier processus dont le nom de commande correspond à un agent connu (claude, codex, amp, opencode). MadoHub lit ensuite ~/.claude/sessions/{PID}.json pour ce PID, qui contient un champ sessionId pointant vers le journal de conversation réel.
Étape 3 : localiser et lire le JSONL
MadoHub récupère le sessionId depuis le fichier de session et parcourt tous les sous-répertoires de ~/.claude/projects/ à la recherche d'un fichier nommé {sessionId}.jsonl — c'est la transcription. MadoHub la parse et en extrait le dernier message de l'assistant — la réponse la plus récente de Claude Code.
Ce message est du texte propre : pas de codes ANSI, pas d'artefacts de terminal, pas d'indicateurs de progression. C'est exactement ce que Claude Code a "dit", dans le format qu'il a prévu.
Étape 4 : quand la résolution échoue
Si le parcours de l'arborescence des processus ne trouve aucun processus d'agent correspondant, ou si le fichier de session a déjà disparu (par exemple si le processus s'est arrêté), MadoHub n'a aucun chemin JSONL disponible pour ce tour. Il se rabat alors sur l'extraction de la sortie terminal — la même approche de suppression des codes ANSI utilisée pour les agents qui ne produisent aucun journal de session.
Ce que permet la réponse propre
Avec une réponse propre et structurée, la transformation de routage peut :
Comprendre le contexte : le modèle de routage IA reçoit le contenu sémantique réel — "J'ai refactorisé le middleware d'auth pour utiliser des jetons JWT au lieu de cookies de session. 3 fichiers modifiés : src/auth/middleware.ts, src/auth/tokens.ts et src/config/auth.ts" — au lieu d'un mélange de sortie terminal colorée.
Générer des prompts précis : "Relis la migration JWT dans src/auth/middleware.ts, src/auth/tokens.ts et src/config/auth.ts. Vérifie que le flux de rafraîchissement des jetons gère correctement l'expiration et que le middleware valide bien les signatures."
Préserver les blocs de code : si la réponse de l'agent contient des extraits de code, ils restent formatés comme à l'origine, sans être corrompus par le retour à la ligne du terminal ou les codes de couleur ANSI.
Cache et performances
Les fichiers JSONL peuvent grossir pendant une longue session. MadoHub met en cache la dernière réponse résolue par ID de tile pour éviter de reparcourir tout le fichier à chaque déclenchement de routage. Le cache est invalidé lorsqu'une nouvelle complétion est détectée.
La chaîne de résolution est également optimisée pour le cas courant : le parcours de l'arborescence des processus et la lecture du fichier de session sont rapides, et la résolution réussit dans l'immense majorité des cas. Parcourir ~/.claude/projects/ à la recherche du fichier {sessionId}.jsonl correspondant est l'étape la plus coûteuse, car elle doit fouiller chaque sous-répertoire de projet.
Limites
La détection d'état basée sur JSONL n'est plus l'apanage de Claude Code — Codex dispose désormais de son propre pipeline de statut adossé à JSONL (task_complete → completed, turn_aborted/error → failed, user_message/task_started/agent_message → working, ri_message → stopped). Là où les agents diffèrent encore, c'est dans l'extraction du texte de la réponse : le format de journal de session propre et structuré décrit dans cet article — lire le dernier message de l'assistant directement depuis la transcription — reste spécifique à Claude Code. Pour Codex et les commandes shell, MadoHub continue de se rabattre sur l'extraction de la sortie terminal avec suppression des codes ANSI pour obtenir le texte réel de la réponse.
Les types de transformation raw et full-output fonctionnent avec n'importe quel agent, car ils utilisent directement la sortie terminal. Les transformations ai-routing et summary donnent les meilleurs résultats avec Claude Code, car elles reçoivent le contenu JSONL propre.
Pourquoi c'est important
La qualité de la sortie de routage est directement proportionnelle à la qualité de la réponse extraite. Garbage in, garbage out. En lisant des journaux de session structurés plutôt qu'en scrappant la sortie terminal, MadoHub obtient la représentation la plus fidèle possible de ce que l'agent source a réellement produit.
C'est une décision de conception aux effets cumulatifs : meilleure extraction → meilleurs prompts de transformation → instructions plus précises pour l'agent cible → moins de cycles perdus → workflow plus rapide. L'investissement dans une extraction propre paie à chaque étape du pipeline de routage.