Skip to main content
Volver al blog
Blog

Bajo el capó: cómo la resolución de sesiones JSONL impulsa el enrutamiento de agentes

Una inmersión profunda en cómo MadoHub lee los registros de sesión de Claude Code para extraer respuestas limpias y estructuradas para el enrutamiento inteligente.

El problema más difícil del enrutamiento de agentes no es generar el prompt para el agente de destino. Es extraer una respuesta limpia del agente de origen.

La salida de terminal es desordenada. Códigos de escape ANSI, spinners de progreso, ajuste de línea, secuencias de color: el scraping bruto de terminal te da una pared de ruido. Puedes eliminar los códigos de control, pero aun así te quedas con artefactos de formato, líneas parciales y contenido mezclado.

MadoHub lo resuelve de otra manera. En lugar de hacer scraping de la salida del terminal, leemos los registros estructurados de sesión de Claude Code: los archivos JSONL que contienen representaciones limpias y semánticas de cada turno de conversación.

Esta entrada explica cómo funciona.

El problema del scraping de terminal

Cuando Claude Code se ejecuta en un terminal, su salida incluye:

  • Secuencias de escape ANSI para colores, posicionamiento del cursor y formato.
  • Marcadores Unicode como ❯ (prompt), ✽ (thinking) y ⏺ (uso de herramientas).
  • Indicadores de progreso que sobrescriben líneas anteriores.
  • Diálogos de permisos con elementos interactivos.
  • Bloques de código multilínea con códigos de escape para resaltado de sintaxis.

Eliminar los códigos ANSI te acerca al texto limpio, pero no lo resuelve del todo. Los indicadores de progreso dejan líneas parciales. Los bloques de código pierden su estructura. La frontera entre "salida del agente" y "ruido del shell" es ambigua.

Para un enrutamiento simple (por ejemplo, "envía las últimas 10 líneas"), el scraping de terminal basta. Pero para el enrutamiento inteligente, donde un modelo de IA necesita entender lo que realmente hizo el agente y generar un prompt contextual, necesitas datos limpios y estructurados.

Cómo guarda Claude Code las sesiones

Claude Code escribe registros de sesión estructurados en formato JSONL (un objeto JSON por línea). La ruta de resolución es:

tmux pane → ps process-tree BFS
  → agent PID (first descendant whose command matches claude/codex/amp/opencode)
    → ~/.claude/sessions/{PID}.json
      → sessionId field
        → ~/.claude/projects/*/{sessionId}.jsonl
          → conversation turns (user/assistant messages)

Cada turno de conversación contiene el rol del mensaje, el contenido y metadatos, sin ningún formato de terminal. Es el mismo dato que Claude Code usa internamente para mantener el contexto de la conversación.

La cadena de resolución

MadoHub resuelve el archivo JSONL mediante una cadena de varios pasos:

Paso 1: obtener el ID del proceso PTY

Cada tile de terminal está respaldado por un pane de tmux, y MadoHub rastrea el PID de ese pane desde el momento en que se crea el terminal. Ese PID es el punto de partida para localizar el proceso real del agente, que normalmente se ejecuta como descendiente del shell del pane.

Paso 2: encontrar el PID del agente y su archivo de sesión

Claude Code mantiene un directorio de sesiones en ~/.claude/sessions/, pero cada archivo lleva el nombre del ID de proceso del agente, no un ID de sesión. Para encontrar ese PID, MadoHub recorre el árbol de procesos desde el pane de tmux (una búsqueda en anchura con ps -eo pid=,ppid=,comm= sobre los descendientes) hasta llegar al primer proceso cuyo nombre de comando coincida con un agente conocido (claude, codex, amp, opencode). Luego MadoHub lee ~/.claude/sessions/{PID}.json para ese PID, que contiene un campo sessionId que apunta al registro de conversación real.

Paso 3: localizar y leer el JSONL

MadoHub toma el sessionId del archivo de sesión y escanea todos los subdirectorios de ~/.claude/projects/ en busca de un archivo llamado {sessionId}.jsonl - ese archivo es la transcripción. MadoHub lo analiza y extrae el último mensaje del asistente: la respuesta más reciente de Claude Code.

Ese mensaje es texto limpio: sin códigos ANSI, sin artefactos de terminal, sin indicadores de progreso. Es exactamente lo que Claude Code "dijo", en el formato en el que lo pretendía.

Paso 4: cuando falla la resolución

Si el recorrido del árbol de procesos no encuentra un proceso de agente coincidente, o si el archivo de sesión ya ha desaparecido (por ejemplo, el proceso terminó), MadoHub no tiene ninguna ruta JSONL disponible para ese turno. En su lugar, recurre a la extracción de la salida del terminal, el mismo enfoque de eliminación de ANSI que se usa para los agentes que no producen ningún registro de sesión.

Lo que habilita la respuesta limpia

Con una respuesta limpia y estructurada, la transformación de enrutamiento puede:

Entender el contexto: el modelo de enrutamiento de IA recibe el contenido semántico real - "He refactorizado el middleware de autenticación para usar tokens JWT en lugar de cookies de sesión. He cambiado 3 archivos: src/auth/middleware.ts, src/auth/tokens.ts y src/config/auth.ts" - en lugar de una masa de salida terminal coloreada.

Generar prompts precisos: "Revisa la migración a tokens JWT en src/auth/middleware.ts, src/auth/tokens.ts y src/config/auth.ts. Verifica que el flujo de renovación de tokens gestione correctamente la expiración y que el middleware valide bien las firmas."

Preservar bloques de código: si la respuesta del agente incluye fragmentos de código, se conservan con su formato original, no corrompidos por el ajuste de línea del terminal ni por los códigos ANSI de color.

Caché y rendimiento

Los archivos JSONL pueden crecer mucho durante una sesión larga. MadoHub cachea la última respuesta resuelta por ID de tile para evitar volver a analizar todo el archivo en cada disparador de enrutamiento. La caché se invalida cuando se detecta una nueva finalización.

La cadena de resolución también está optimizada para el caso común: el recorrido del árbol de procesos y la lectura del archivo de sesión son rápidos, y la resolución tiene éxito en la inmensa mayoría de los casos. Escanear ~/.claude/projects/ en busca del archivo {sessionId}.jsonl correspondiente es el paso más costoso, ya que tiene que buscar en cada subdirectorio de proyecto.

Limitaciones

La detección de estado basada en JSONL ya no es exclusiva de Claude Code: Codex ahora tiene su propio pipeline de estado respaldado por JSONL (task_complete → completed, turn_aborted/error → failed, user_message/task_started/agent_message → working, ri_message → stopped). Donde los agentes todavía difieren es en la extracción del texto de la respuesta: el formato de registro de sesión limpio y estructurado descrito en esta entrada - leer el último mensaje del asistente directamente de la transcripción - es específico de Claude Code. Para Codex y los comandos de shell, MadoHub sigue recurriendo a la extracción de la salida del terminal con eliminación de ANSI para obtener el texto real de la respuesta.

Los tipos de transformación raw y full-output funcionan con cualquier agente porque usan directamente la salida del terminal. Los tipos ai-routing y summary producen los mejores resultados con Claude Code porque reciben el contenido limpio del JSONL.

Por qué importa

La calidad de la salida de enrutamiento es directamente proporcional a la calidad de la respuesta extraída. Basura entra, basura sale. Al leer registros de sesión estructurados en lugar de hacer scraping del terminal, MadoHub obtiene la representación de mayor fidelidad de lo que realmente produjo el agente de origen.

Es una decisión de diseño con beneficios acumulativos. Mejor extracción → mejores prompts de transformación → instrucciones más precisas para el agente de destino → menos ciclos desperdiciados → flujo de trabajo más rápido.