«command not found» en la terminal del agente: cómo Hermes arregló un PATH que los login shells reiniciaban

Le pides al agente que ejecute hermes cron list en su terminal para ver por qué un job programado nunca se disparó. Te contesta con una sola línea: bash: hermes: command not found. Abres tu propia ventana SSH, escribes lo mismo y funciona. De vuelta en la sesión, command -v node también sale vacío, aunque instalaste node con nvm.
No es un problema de permisos y Hermes no está roto. Algo reinició el PATH, en silencio y por completo.
Quién reinició el PATH
Detrás de la herramienta de terminal hay un bash. Para que los setups de nvm, asdf y pyenv que usas también sirvan dentro del agente, Hermes abre un login shell una vez al crear la sesión y captura el entorno resultante: cargar ~/.profile, ~/.bash_profile y ~/.bashrc es precisamente como esos gestores de versiones se añaden al PATH. Esa parte es intencionada y útil.
El problema está en el /etc/profile de Debian y Ubuntu: no amplía el PATH, lo reescribe con una lista fija (normalmente /usr/local/bin:/usr/bin:/bin más unos cuantos directorios de juegos). En cuanto el login shell termina, la instantánea capturada fija ese PATH reiniciado. Todo lo que Hermes tenía al arrancar desaparece: ~/.local/bin, /opt/hermes/bin, /opt/hermes/.venv/bin y cada directorio de shims de nvm.
En la imagen Docker el síntoma es el más escandaloso: entra en un contenedor con bash -l, o con docker exec … bash -l, y un simple hermes sale con código 127. Los comandos en segundo plano sufren lo mismo, porque pasan por bash -lic.
La solución: reañadir lo que el profile descartó
La reparación no pisa lo que decidió el login shell, sino que concilia ambas cosas:
- después de ejecutarse el login profile, cada directorio del entorno de lanzamiento que ahora falta se añade al final, respetando su orden relativo original;
- los directorios que ya están presentes nunca se duplican;
- solo se comprueba la existencia de entradas absolutas: una entrada relativa se resuelve contra el directorio de trabajo del shell, así que verificarla desde Python no tendría sentido;
- los directorios inexistentes se omiten en lugar de meterse en el PATH como peso muerto.
Añadir al final es la decisión importante. Lo que hayan decidido tu init de nvm o de pyenv se queda exactamente donde estaba, así que un comando que antes se resolvía sigue apuntando al mismo binario; solo los comandos que no tenían ninguna coincidencia ganan una.
Los comandos en segundo plano (spawn_local de process_registry, bash -lic) anteponen el mismo fragmento de restauración, de modo que primer y segundo plano no pueden discrepar sobre qué python o qué hermes existe.
La imagen Docker suma una segunda capa: /etc/profile.d/hermes.sh se instala dentro de la imagen, así que cada login shell vuelve a añadir los tres directorios de la imagen, dejando donde están los que ya figuraban en la lista.
# /etc/profile.d/hermes.sh (dentro de la imagen)
for __hermes_p in /opt/data/.local/bin /opt/hermes/.venv/bin /opt/hermes/bin; do
case ":$PATH:" in
*":$__hermes_p:"*) ;;
*) PATH="$__hermes_p:$PATH" ;;
esac
done
unset __hermes_p
export PATH
Comprueba cuál es el PATH real
Tres líneas dentro de la terminal del agente te cuentan toda la historia:
echo $PATH # lo que recibe realmente el agente
command -v hermes # vacío significa que no lo encuentra
bash -lc 'echo $PATH' # el PATH reiniciado del login shell, para comparar
Tabla de síntoma a causa:
| Síntoma | Causa real | Qué hacer |
|---|---|---|
hermes: command not found dentro de Docker |
El login shell reinició el PATH; a la imagen le falta el parche de profile.d | Actualiza la imagen o la build de Hermes |
Falta node / python aunque estén instalados en el host |
/etc/profile machacó el init de nvm/pyenv que venía de ~/.profile |
Corregido automáticamente en la build nueva; en builds antiguas, lista los archivos de init explícitamente |
| Solo fallan los comandos en segundo plano | La build antigua no compartía el fragmento de restauración con la ruta de segundo plano | Actualiza, o usa comandos en primer plano mientras tanto |
| Tu propio script no encuentra una herramienta | El script contaba con un directorio que solo tiene un shell interactivo | Usa rutas absolutas o añade el archivo a shell_init_files |
Si tienes archivos de entorno propios —un toolchain interno, por ejemplo—, dile a Hermes exactamente qué cargar en vez de confiar en que lo adivine:
terminal:
auto_source_bashrc: true # por defecto; una lista shell_init_files explícita toma el control
shell_init_files:
- ~/.profile
- ~/.config/hermes/env.sh
Límites que conviene conocer
- Solo se restauran los directorios que existían en el entorno con el que se lanzó Hermes. Lo que tu proceso padre nunca tuvo no se inventa.
- Si quitaste a propósito un directorio del PATH dentro de tu profile pero seguía en el PATH de lanzamiento, vuelve. Es el compromiso asumido: mantener funcionando los comandos que ya funcionaban.
- Windows no se ve afectado: el PATH nativo se une con punto y coma y bash recibe una copia convertida.
- El orden es precedencia. Como las entradas restauradas caen al final, los directorios del sistema siguen yendo antes que los tuyos; no “arregles” problemas de PATH poniendo un directorio escribible en cabeza.
Para una revisión de entorno más amplia, ejecuta hermes doctor. El comportamiento del espacio de trabajo temporal se trata en directorios temporales de la terminal, los layouts de contenedor en modo contenedor compartido y el cambio de shell completo en plugins de backend de terminal.
Estado del release
El arreglo se integró en main el 29 de octubre de 2026 (PR #146080). El release actual, v0.22.5 (23 de octubre de 2026), no lo incluye, así que las builds anteriores todavía pueden toparse con command not found.
Los bugs de PATH son desagradables justamente porque nunca lanzan un error: simplemente faltan unos cuantos directorios. Al menos ahora vuelven a donde les corresponde.