Hermes Agent: 7 dolares por nada
Introducción
Instalé Hermes Agent en mi servidor para vigilar dependencias desactualizadas en mis PRs de GitHub sin tener que estar pendiente: usuario dedicado sin sudo, systemd hardened, toolset recortado a 9 herramientas de solo lectura, cron cada 5 minutos en vez de webhook público. Lo que no hardening fue cuánto me iba a costar correrlo sin supervisión.
Cuatro días después, el dashboard de DeepSeek mostraba el balance en -$0.01 — de $20 cargados no quedaba nada — y el agente no había reportado ni una sola dependencia. Esto es la reconstrucción de qué pasó, con los números reales.

Cómo debía funcionar
Flujo esperado del vigía de dependencias
Cron dispara cada 5 minutos
Trigger programado, sin webhook público.
Lista PRs abiertos
mcp__github__list_pull_requests
Lee manifiestos de cada PR
package.json, requirements.txt, go.mod, etc. vía get_file_contents
Compara versiones
Contra el registro correspondiente (npm, PyPI, crates.io)
¿Hay dependencias desactualizadas?
Si no, termina en silencio ([SILENT])
Genera reporte interno
Nota local o notificación, sin comentar en el PR
El síntoma: créditos en cero sin haber visto un solo resultado
No lo noté por una alerta ni por un log — lo noté porque entré al panel de DeepSeek por otra razón y vi esto:

El gráfico de gasto por día mostraba algo bien claro: cero actividad durante semanas, y de golpe un pico vertical los últimos 4-5 días que se comió todo el saldo. La columna "API Key" del dashboard señalaba directo a Hermes-Bot.
Mi primera reacción fue de sospecha de compromiso — el mismo instinto que tuve con lo de Gitea: algo externo abusando de una credencial mía. Pero acá no había nada expuesto a internet, el token estaba scopeado, y el agente corría en un usuario sin privilegios. Tenía que ser otra cosa.
La investigación: cuando la propia herramienta te miente
Lo primero que corrí fue lo obvio:
hermes cron runs github-dependency-watch
No cron execution attempts recorded.
Según esto, el job nunca había corrido. Eso no cuadraba con nada — el gasto era real, la API key era real, y el gráfico de DeepSeek mostraba tráfico sostenido durante días. hermes cron status sí confirmaba que el scheduler estaba vivo (Ticker heartbeat: 18s ago, Next run avanzando cada 5 minutos), así que el comando de historial estaba simplemente roto o no trackeaba lo que yo esperaba. Tuve que ir a otra fuente.
hermes insights
Ahí apareció la foto completa:
Overview
| Métrica | Valor |
|---|---|
| Sessions | 817 |
| Messages | 22,474 |
| Tool calls | 13,525 |
| User messages | 817 |
| Input tokens | 11,096,194 |
| Output tokens | 4,121,282 |
| Total tokens | 138,731,076 |
Platforms
| Platform | Sessions | Messages | Tokens |
|---|---|---|---|
| cron | 816 | 22,472 | 136,477,098 |
| cli | 1 | 2 | 6,126 |
816 sesiones cron en poco más de 4 días. La sesión más pesada llegó a 52 tool calls y 61,683 tokens en una sola corrida. Y el desglose de herramientas usadas fue lo que terminó de explicar todo:
Top Tools
| Tool | Calls | % |
|---|---|---|
| terminal | 4,370 | 27.3% |
| tool_describe | 3,542 | 22.1% |
| tool_call | 2,527 | 15.8% |
| mcp__github__list_pull_requests | 1,279 | 8.0% |
| tool_search | 1,081 | 6.8% |
| read_file | 1,017 | 6.4% |
| search_files | 909 | 5.7% |
| mcp__github__get_me | 747 | 4.7% |
terminal + tool_describe + tool_call + tool_search suman 71.9% de todas las llamadas. Eso no es "leer PR, detectar dependencia vieja" — es el agente abriendo un entorno de terminal, describiendo y buscando herramientas disponibles, en cada una de las 816 corridas, antes de siquiera tocar GitHub. El trabajo real (mcp__github__list_pull_requests, pull_request_read, get_file_contents) es una fracción chica del total.
Adónde fue el gasto realmente
Cron (5m)
Hermes Agent
terminal / describe / search
72% del gasto, 0% trabajo real
GitHub MCP
28% del gasto, trabajo real
DeepSeek API
$7.25 cobrados en total
Revisando los logs de la primera corrida (guardados de cuando armé el job), ya se veía la pista: agent returned [SILENT] — skipping delivery. El agente corría, gastaba tokens abriendo terminal y describiendo tools, y terminaba sin encontrar ninguna dependencia que reportar. Se repitió así, en automático, cada 5 minutos, durante 4 días.
La confirmación: el dashboard real vs. la estimación de Hermes
hermes insights trae su propia estimación de costo: ~$3.05, con una advertencia de "60 sessions (no pricing data)". El dashboard real de DeepSeek marcó $7.25 en el mismo período — más del doble de lo que la herramienta local calculaba. La diferencia probablemente viene de que DeepSeek metió un esquema de precios peak/off-peak a mediados de agosto, y el estimador local de Hermes no lo tiene actualizado. Si hubiera confiado solo en el número que me mostraba hermes insights, habría subestimado el problema por completo.
Estimación local vs. dashboard real
hermes insights (estimado)
- ~$3.05
- 60 sessions sin pricing data
- Basado en tarifas desactualizadas
Dashboard DeepSeek (real)
- $7.25
- Fuente de verdad del proveedor
- Incluye esquema peak/off-peak de agosto
Timeline del incidente
Del alta del cron a la contención
21 ago, ~15:40
Creo github-dependency-watch: every 5m, --continuity, sin límite ni tope de gasto
21 ago, 15:52
Primera corrida: [SILENT] — skipping delivery, ya usó terminal varias veces
21–24 ago
El job dispara cada 5 min sin supervisión, sesión tras sesión
25 ago, noche
Reviso el dashboard por otra razón: balance en -$0.01, cero dependencias reportadas
25 ago (detección)
hermes cron runs dice 'sin ejecuciones' (falso); hermes insights revela 817 sesiones, 138.7M tokens, 71.9% overhead
25 ago (contención)
hermes cron pause github-dependency-watch: job pausado
La contención: parar el sangrado primero, investigar después
hermes cron pause github-dependency-watch
No hubo mucho más que decidir en este paso — el error no era de seguridad (nadie más tenía la API key, nada estaba comprometido), era de diseño de automatización: un intervalo demasiado agresivo, sin tope de costo, y un toolset más amplio del que la tarea necesitaba.
El hardening: lo que voy a cambiar antes de reactivarlo
Todavía estoy validando la forma correcta de aplicar algunos de estos puntos en Hermes — lo dejo anotado tal cual, sin inventar sintaxis que no confirmé:
- Intervalo mucho más largo. 5 minutos no tiene sentido para "revisar si hay dependencias nuevas en PRs abiertos" — cada 30-60 minutos alcanza sobrado para este caso de uso, y corta el volumen de sesiones a una fracción.
- Sacar
terminaldel alcance de este job. El flujo de "leer PR y detectar dependencia vieja" no debería necesitar terminal para nada — solo el MCP de GitHub. Voy a revisar si Hermes permite acotar el toolset por cron job (hermes profile, o algo equivalente) en vez de heredar todo lo habilitado globalmente. - Tope de gasto real, no solo estimación local. Configurar el límite de balance/alertas directo en el dashboard de DeepSeek (ya lo tenía como "disabled" — irónico) en vez de confiar en que
hermes insightsme avise a tiempo. - Reusar la infraestructura de ntfy que ya tengo (armada después del incidente de Gitea) para un watcher de costo: si el gasto diario de una API key supera un umbral, que me llegue una notificación al celular en vez de enterarme por casualidad cuatro días después.
- Revisar
--continuityde cerca. La idea era que cada corrida supiera qué dependencias ya se habían reportado y no repitiera trabajo — pero si cada corrida sigue gastando tool calls en describir/buscar herramientas desde cero,--continuityno está evitando el overhead real, solo el contenido del reporte.
- Un cron sin tope de costo es un cheque en blanco, sin importar cuánto hayas hardened el usuario del sistema. El aislamiento de proceso no te protege de un loop que gasta plata legítimamente, con tus propias credenciales, haciendo exactamente lo que le pediste (aunque de forma ineficiente).
- Las herramientas de observabilidad de la propia plataforma pueden mentir o subestimar.
hermes cron runsdecía "cero ejecuciones" con 816 corridas reales.hermes insightsestimaba $3.05 contra $7.25 reales. Cuando algo no cuadra, la fuente de verdad es el proveedor final (el dashboard de DeepSeek), no la capa intermedia. - "Silencioso" no es gratis. Un agente que corre, no encuentra nada que hacer, y termina sin avisar (
[SILENT]) igual gastó tokens llegando a esa conclusión — 816 veces. - El intervalo de un cron es una decisión de costo, no solo de latencia. Elegí 5 minutos pensando en "que detecte rápido", sin calcular cuántas corridas eso significa en un mes ni cuánto cuesta cada una, aunque sea barata individualmente.
Conclusión
Nadie entró a nada esta vez, pero el resultado fue el mismo que con el minero en Gitea: algo corrió sin que yo lo estuviera mirando, y me enteré tarde. Ahí faltaba un candado en la puerta; acá faltaba un límite de gasto en el cron.
La próxima vez, el tope de gasto y la alerta van en el setup inicial, no después de vaciar el balance. Barato no es lo mismo que gratis.
"Trust, but verify."

