El aviso de aprobación que antes se esfumaba a mitad de café ahora te espera

Le pides a tu agent que borre una caché de compilación que no has tocado en meses. Te pregunta si puede ejecutar la orden. Pulsas Enter y te vas a rellenar la taza de café. Cuando vuelves, el aviso ha desaparecido, la orden nunca se ejecutó y la transcripción dice una sola cosa: aprobación agotada por tiempo. No respondiste mal — simplemente el reloj fue más rápido que tú.
Actualización (2026-10-03): El cambio se revirtió esa misma noche (PR #130826: la espera infinita ya se había rechazado como dirección y dejaba colgado
hermes chat -q, donde nadie puede responder). Los avisos de aprobación y de clarify en el CLI, el TUI y el Desktop vuelven a regirse porapprovals.timeoutyagent.clarify_timeout. Motivos completos y qué hacer en su lugar: la nota de reversión.
Ese reloj era approvals.timeout, 300 segundos por defecto. Ahora se divide según una línea muy simple: ¿hay alguien mirando la pantalla? Los avisos de CLI, TUI y Desktop se mantienen visibles hasta que respondas, interrumpas el turno o cierres la sesión. Las plataformas de mensajería conservan su plazo. Esto es lo que ha cambiado y lo que significa para tu configuración.
Primero, el muro de los cinco minutos
Las aprobaciones se rigen por una regla innegociable: un timeout no es un sí. No es «nadie respondió, así que adelante», sino «nadie respondió, así que no». Es el valor seguro por defecto, pero te cuesta una acción descartada en silencio, y en la transcripción se ve casi idéntica a «la orden ha fallado».
¿Por qué cinco minutos? Los comentarios de configuración son sorprendentemente claros al respecto: las aprobaciones en plataformas de mensajería llegan como una notificación push, y 60 segundos resultaron demasiado justos — en Telegram y Discord el aviso a menudo caducaba antes de que el usuario llegara al móvil. Así que el valor por defecto se subió a 300.
El problema es que 300 segundos tienen sentido para una notificación push y ninguno cuando la persona ya está sentada frente al aviso. Te levantaste a por un vaso de agua, y lo que encontraste al volver no fue «sigue esperando», sino «ya lo has perdido».
La nueva regla, dividida por superficie
| Superficie | Cuánto tiempo permanece abierto el aviso |
|---|---|
CLI interactiva (hermes chat) |
hasta que respondas o interrumpas el turno |
| TUI | igual |
| App de escritorio | igual |
| Telegram / Discord / Slack y otras plataformas de mensajería | sigue siendo approvals.timeout (300 s por defecto) |
| Editores ACP y plugins de transporte de aprobaciones | sigue siendo approvals.timeout |
La línea divisoria no es qué superficie es más sofisticada, sino si el usuario puede ver el aviso al instante. En una plataforma de mensajería, una aprobación es un push que puede quedarse sin leer durante horas, y los propios botones de la plataforma caducan. Esas superficies necesitan un plazo. En la CLI, la TUI y el escritorio, el aviso se dibuja en la pantalla que ya estás mirando, así que no hay nada que perder.
Un límite que conviene tener presente: no se ejecuta nada mientras espera. El contrato no ha cambiado — esto sigue siendo fail-closed, ninguna herramienta se libera mientras el aviso está en pantalla, e interrumpirlo cuenta como una denegación.
Cómo está conectado
El cambio toca varios módulos, pero el núcleo es un interruptor que se enlaza una vez por turno:
| Dónde | Qué hace |
|---|---|
tools/approval_context.py |
añade set_prompts_wait_for_answer() / reset_prompts_wait_for_answer() tras una variable de contexto por turno, y convierte approval_wait_seconds() en el único resolutor de la ventana de aprobación integrada |
agent/deadline.py |
MAX_SAFE_TIMEOUT_S: 365 días fuera de Windows, y en Windows el techo en milisegundos de un DWORD (unos 49,7 días) — ambos semánticamente «sin límite», dimensionados para que los límites de Lock.acquire / Thread.join derivados de él no puedan desbordarse |
tools/approval_human_wait.py |
human_wait_ceiling() devuelve la ventana de aprobación más un margen de 60 segundos, de modo que el bucle de plazo por lotes y el lock de serialización de la puerta de autorización comparten un único techo en vez de inventarse cada uno el suyo |
hermes_cli/cli_chat_turn_mixin.py |
enlaza el interruptor en torno a cada turno y lo restablece al desmontar |
tui_gateway/prompt_turn.py |
la TUI y el escritorio comparten este backend: se enlaza cuando se prepara la entrada del turno y se restablece al salir |
De paso se corrigió un bug sutil. El bucle de plazo por lotes podía leer el techo de espera humana desde un contexto que nunca vio el enlace, lo que haría que un aviso sin límite pareciera una ventana bloqueada y seguiría prolongando el plazo del lote. Ahora lee el techo real, y un aviso sin límite se excluye de plano en lugar de medirse mal. Las ventanas de mensajería siguen recortadas exactamente igual que antes.
La excepción que hay que recordar: el modo de consulta única
hermes chat -q "..." no tiene a nadie sentado al lado, así que deliberadamente no enlaza el interruptor y sigue usando approvals.timeout. Normalmente no espera en absoluto: approvals.single_query_mode tiene deny por defecto, de modo que una orden peligrosa se rechaza de inmediato y se empuja al agent a buscar otra vía.
Si ejecutas sesiones de consulta única en scripts o CI, no cuentes con «esperar más tiempo» — nunca hubo nadie ahí para pulsar la tecla.
Qué revisar en tu máquina
- El uso interactivo ya no necesita un timeout elevado. Si habías subido
approvals.timeouta miles de segundos solo para darte margen, puedes volver a bajarlo: ahora solo rige la mensajería y los transportes ACP y de plugins. - Ajusta la mensajería por separado.
hermes config set approvals.timeout 600solo afecta a esas superficies; la clave está documentada junto con el resto de la configuración en la referencia de configuración de hermes. - Deja de esperar que un timeout signifique sí. Para reducir las interrupciones, usa las aprobaciones inteligentes; para abrirlo todo, existen los niveles yolo; para prohibir clases enteras de órdenes, usa la lista de denegación. El panorama completo está en la guía de aprobaciones desatendidas.
- Haz un dry-run antes de cambiar la política. Los dry-runs y las autocomprobaciones de aprobaciones muestran qué regla coincide con qué veredicto sin ejecutar nada.
- Cron y las plataformas desatendidas siguen denegando. Sus valores por defecto no han cambiado, y eso es precisamente lo que se busca.
La conclusión
Lo que parece «el aviso deja de desaparecer» es en realidad un único parámetro dividido en dos situaciones: si hay una persona mirando, no hay plazo; si no mira nadie, tiene que haberlo. El primer caso te evita perder una acción por culpa de tu propia pausa para el café. El segundo mantiene honesta la ruta de las notificaciones push.
La regla general es lo bastante corta para memorizarla: si el aviso se dibuja en la pantalla que estás mirando, te espera; si se envió a tu móvil, tiene que caducar.