Documentação oficial desta página · v0.20.6
Configuração do Hermes Agent
Todas as configurações ficam no diretório ~/.hermes/, fácil de achar.
config.yaml que funcionaRode hermes setup --portal — um único OAuth te dá um provedor de modelo e as quatro ferramentas do Tool Gateway sem editar YAML na mão. Quem assina o Portal ainda ganha 10% de desconto nos provedores cobrados por token. Veja Nous Portal.
Estrutura de diretórios
~/.hermes/
├── config.yaml # Configurações (modelo, terminal, TTS, compressão, etc.)
├── .env # Chaves de API e segredos
├── auth.json # Credenciais OAuth de provedores (Nous Portal, etc.)
├── SOUL.md # Identidade principal do agente (posição #1 no prompt de sistema)
├── memories/ # Memória persistente (MEMORY.md, USER.md)
├── skills/ # Skills criadas pelo agente (gerenciadas pela ferramenta skill_manage)
├── cron/ # Tarefas agendadas
├── sessions/ # Sessões do gateway
└── logs/ # Logs (errors.log, gateway.log — segredos são ocultados automaticamente)
Gerenciar a configuração
hermes config # Mostra a configuração atual
hermes config edit # Abre o config.yaml no seu editor
hermes config get KEY # Imprime um valor já resolvido
hermes config set KEY VAL # Define um valor específico
hermes config unset KEY # Remove um valor definido pelo usuário
hermes config check # Procura opções faltando (depois de atualizar)
hermes config migrate # Acrescenta as opções faltando, de forma interativa
# Exemplos:
hermes config get model
hermes config set model anthropic/claude-opus-4
hermes config set terminal.backend docker
hermes config unset terminal.backend
hermes config set OPENROUTER_API_KEY sk-or-... # Vai para o .env
O comando hermes config set manda cada valor para o arquivo certo automaticamente — chaves de API vão para o .env, todo o resto para o config.yaml.
Precedência da configuração
As configurações são resolvidas nesta ordem (da maior prioridade para a menor):
- Argumentos da CLI — por exemplo,
hermes chat --model anthropic/claude-sonnet-4(sobrescreve só naquela chamada) ~/.hermes/config.yaml— o arquivo principal para tudo que não é segredo~/.hermes/.env— reserva para variáveis de ambiente; obrigatório para segredos (chaves de API, tokens, senhas)- Padrões embutidos — valores seguros no código quando nada mais foi definido
Segredos (chaves de API, tokens de bot, senhas) vão no .env. Todo o resto (modelo, backend de terminal, configurações de compressão, limites de memória, conjuntos de ferramentas) vai no config.yaml. Quando os dois definem a mesma coisa, o config.yaml vence nas configurações que não são segredo.
Um administrador pode fixar valores de configuração e de segredos que um usuário comum não consegue sobrescrever, usando um diretório gerenciado em nível de sistema. Veja Escopo gerenciado.
Limites de runtime
As superfícies de servidor do Hermes que ficam rodando por muito tempo (incluindo o gateway e o
hermes serve --isolated) aplicam o limite flexível RLIMIT_NOFILE configurado
durante a inicialização, quando o sistema operacional permite:
runtime:
nofile_soft_limit: 4096
O padrão é 4096. O Hermes limita o alvo ao teto rígido do sistema
operacional e nunca rebaixa um processo que já tem um limite flexível maior. Defina o
valor como 0, false ou null para desligar o ajuste. No Windows e em sandboxes
onde o limite não pode ser alterado, a inicialização continua sem mexer no
limite.
Configurações do banco de dados
A seção database: controla como o Hermes abre o banco de estado SQLite
(state.db), que guarda sessões, mensagens e o roteamento do gateway:
database:
# Modo de journal do state.db: wal (padrão) ou delete.
# Use delete em sistemas de arquivos onde o WAL não é seguro (montagens de
# rede, algumas configurações de virtiofs). Atenção: um banco que já está em
# WAL no disco nunca é rebaixado em tempo de execução — o Hermes mantém WAL e
# registra um erro avisando que o delete configurado não foi aplicado. Para
# converter um banco existente, pare todo processo que o usa e rode uma única
# vez, offline, um `PRAGMA journal_mode=DELETE` no arquivo.
journal_mode: wal
# Nível de durabilidade de toda conexão com o state.db: OFF, NORMAL, FULL,
# EXTRA (ou 0-3). Se não definir, fica o padrão de compilação do SQLite, que
# muda de build para build do interpretador. No macOS isto é um piso, não um
# valor fixo: valores abaixo de FULL são recusados para proteger contra o
# reordenamento de fsync do Darwin; EXTRA é respeitado.
# synchronous: FULL
# Pragmas opcionais de dimensionamento do WAL (inteiros). Sem definir = padrões do SQLite.
# wal_autocheckpoint: 1000 # páginas entre checkpoints automáticos
# journal_size_limit: 67108864 # teto para o tamanho do WAL/journal, em bytes
O Hermes também avisa (uma vez por processo, por banco) quando o modo de journal
em disco de um banco existente é trocado silenciosamente para WAL na abertura — por
exemplo, um banco que um operador tinha convertido na mão para delete — e
aponta database.journal_mode como a configuração que faz a escolha valer.
Substituição de variáveis de ambiente
Você pode referenciar variáveis de ambiente no config.yaml com a sintaxe ${VAR_NAME}:
auxiliary:
vision:
api_key: ${GOOGLE_API_KEY}
base_url: ${CUSTOM_VISION_URL}
delegation:
api_key: ${DELEGATION_KEY}
Várias referências num mesmo valor funcionam: url: "${HOST}:${PORT}". Se a variável referenciada não estiver definida, o marcador é mantido literalmente (${UNDEFINED_VAR} fica como está) e um aviso é registrado. $VAR sozinho não é expandido.
A sintaxe SecretRef no estilo do Cursor também é aceita: ${env:VAR_NAME} resolve exatamente como ${VAR_NAME} (o prefixo env: é removido), então trechos de MCP ou de provedor copiados de configurações do Cursor / Claude funcionam sem alteração tanto no config.yaml quanto no bloco mcp_servers. As outras fontes de SecretRef (${file:...}, ${vault:...}, ${bitwarden:...}) não são resolvidas na hora — backends externos de segredo injetam seus valores no ambiente durante a inicialização, pelo bloco secrets:, então referencie-os como ${env:NAME}; prefixos desconhecidos avisam uma vez e ficam literais.
Para configurar provedores de IA (OpenRouter, Anthropic, Copilot, endpoints personalizados, LLMs auto-hospedados, modelos de reserva, etc.), veja Provedores de IA.
Tempos limite de provedor
Você pode definir providers.<id>.request_timeout_seconds para um tempo limite de requisição válido em todo o provedor, e ainda providers.<id>.models.<model>.timeout_seconds para sobrescrever por modelo. Vale para o cliente principal do turno em todo transporte (OpenAI-wire, Anthropic nativo, compatível com Anthropic), para a cadeia de reserva, para reconstruções depois de rotação de credencial e (no OpenAI-wire) para o argumento de tempo limite por requisição — então o valor configurado vence a variável de ambiente antiga HERMES_API_TIMEOUT.
Você também pode definir providers.<id>.stale_timeout_seconds para o detector de chamadas travadas sem streaming, e providers.<id>.models.<model>.stale_timeout_seconds para sobrescrever por modelo. Isso vence a variável de ambiente antiga HERMES_API_CALL_STALE_TIMEOUT.
Deixar essas opções sem definir mantém os padrões antigos (HERMES_API_TIMEOUT=1800s, HERMES_API_CALL_STALE_TIMEOUT=90s, Anthropic nativo 900s). O detector de travamento sem streaming é desligado automaticamente para endpoints locais quando fica implícito, e pode aumentar para contextos muito grandes. Ainda não está ligado ao AWS Bedrock (tanto o caminho bedrock_converse quanto o do SDK AnthropicBedrock usam boto3, com a própria configuração de tempo limite). Veja o exemplo comentado em cli-config.yaml.example.
Comportamento de atualização
As configurações do hermes update ficam sob updates no config.yaml:
updates:
pre_update_backup: quick # quick (instantâneo do estado, padrão) | full (instantâneo + zip do HERMES_HOME) | off
backup_keep: 5 # Mantém esta quantidade de zips completos de backup pré-atualização
non_interactive_local_changes: stash # stash | discard
auto_switch_parked_branch: true # volta sozinho para a main quando o branch parado está limpo e já foi mesclado
pre_update_backup é o único botão de segurança pré-atualização: quick (padrão) tira um instantâneo dos arquivos de estado críticos (dados de pareamento, tarefas de cron, configuração, autenticação; arquivos acima de 1 GiB são pulados) em state-snapshots/; full ainda zipa todo o HERMES_HOME em backups/ e pode levar minutos em diretórios grandes; off desliga os dois. Booleanos antigos continuam válidos (true → full, false → off).
Em instalações via git, o Hermes guarda automaticamente num stash os arquivos rastreados sujos e os arquivos não rastreados antes de trocar para o branch de atualização ou fazer pull. Atualizações interativas no terminal perguntam antes de restaurar esse stash. Atualizações não interativas (aplicativo desktop/chat, gateway ou --yes) usam updates.non_interactive_local_changes: stash restaura as edições locais de código depois de um pull bem-sucedido, enquanto discard descarta o stash criado pela atualização depois de um pull bem-sucedido. Use discard só em instalações gerenciadas onde edições locais de código nunca devem persistir.
Antes desse passo de stash, o Hermes também restaura as diferenças rastreadas de package-lock.json deixadas pela agitação do npm install/build. Faça commit ou guarde manualmente num stash as edições intencionais de lockfile antes de atualizar.
Configuração do backend de terminal
O Hermes suporta sete backends de terminal. Cada um determina onde os comandos de shell do agente realmente rodam — sua máquina local, um contêiner Docker, um servidor remoto por SSH, um sandbox na nuvem da Modal (direto ou pelo gateway gerenciado pela Nous), um espaço de trabalho Daytona, um Vercel Sandbox ou um contêiner Singularity/Apptainer.
terminal:
backend: local # local | docker | ssh | modal | daytona | vercel_sandbox | singularity
cwd: "." # Diretório de trabalho do gateway/cron (a CLI sempre usa o diretório de onde foi chamada)
font_family: "" # Fonte do terminal do desktop; por exemplo "MesloLGS NF"
timeout: 180 # Tempo limite por comando, em segundos
home_mode: auto # auto | real | profile — política de HOME dos subprocessos
env_passthrough: [] # Nomes de variáveis de ambiente a repassar para a execução em sandbox (terminal + execute_code)
singularity_image: "docker://nikolaik/python-nodejs:python3.11-nodejs20" # Imagem de contêiner do backend Singularity
modal_image: "nikolaik/python-nodejs:python3.11-nodejs20" # Imagem de contêiner do backend Modal
daytona_image: "nikolaik/python-nodejs:python3.11-nodejs20" # Imagem de contêiner do backend Daytona
terminal.font_family controla o terminal embutido no Hermes Desktop. Aceita o nome de uma família instalada localmente (por exemplo, MesloLGS NF) ou uma pilha de fontes CSS. O Hermes acrescenta a própria pilha JetBrains Mono como reserva, e um valor vazio mantém o padrão. Você pode editar a mesma configuração, no escopo do perfil, em Settings → Appearance → Terminal Font; não precisa baixar do Google Fonts nem dar permissão de fonte do sistema.
Em sandboxes na nuvem como Modal, Daytona e Vercel Sandbox, container_persistent: true significa que o Hermes vai tentar preservar o estado do sistema de arquivos quando o sandbox for recriado. Não promete que o mesmo sandbox vivo, o mesmo espaço de PID ou os processos em segundo plano ainda estarão rodando depois.
Visão geral dos backends
| Backend | Onde os comandos rodam | Isolamento | Melhor para |
|---|---|---|---|
| local | Direto na sua máquina | Nenhum | Desenvolvimento, uso pessoal |
| docker | Um único contêiner Docker persistente (compartilhado entre sessão, /new e subagentes) | Total (namespaces, cap-drop) | Sandbox seguro, CI/CD |
| ssh | Servidor remoto por SSH | Fronteira de rede | Desenvolvimento remoto, hardware potente |
| modal | Sandbox na nuvem da Modal | Total (VM na nuvem) | Computação efêmera na nuvem, avaliações |
| daytona | Espaço de trabalho Daytona | Total (contêiner na nuvem) | Ambientes de desenvolvimento gerenciados na nuvem |
| vercel_sandbox | Vercel Sandbox | Total (microVM na nuvem) | Execução na nuvem com sistema de arquivos persistente por snapshot |
| singularity | Contêiner Singularity/Apptainer | Namespaces (--containall) | Clusters de HPC, máquinas compartilhadas |
Backend local
O padrão. Os comandos rodam direto na sua máquina, sem isolamento. Não exige configuração nenhuma.
terminal:
backend: local
Por padrão, os subprocessos locais de ferramenta mantêm o HOME real do seu usuário do sistema. Isso deixa
CLIs externas como git, ssh, gh, az, npm, Claude Code e Codex
encontrarem as credenciais e a configuração que já usam no seu shell normal. O estado do Hermes
continua no escopo do perfil, via HERMES_HOME; não é o HOME que decide de onde os perfis
pegam configuração, memória, sessões ou skills.
O Hermes não muda o seu HOME global, os arquivos de inicialização do shell nem
o diretório home da conta do sistema. Esta configuração só controla o ambiente
passado aos subprocessos que o Hermes lança por ferramentas como terminal,
processos de terminal em segundo plano, execute_code e processos auxiliares do ACP.
terminal.home_mode
| Modo | Instalações no host | Contêineres | Contrapartida |
|---|---|---|---|
auto | Mantém o HOME real do usuário do sistema | Usa {HERMES_HOME}/home | Padrão recomendado. As CLIs do host continuam funcionando; o estado do contêiner persiste. |
real | Força o HOME real do usuário do sistema | Força o HOME real do usuário do sistema, se visível | Útil quando um processo pai começou sem querer com o HOME apontando para o home de um perfil. |
profile | Usa {HERMES_HOME}/home quando ele existe | Usa {HERMES_HOME}/home quando ele existe | Isolamento estrito da configuração de CLI por perfil, mas ~/.ssh, ~/.gitconfig, ~/.azure, ~/.config/gh, autenticação do Claude/Codex, estado do npm etc. deixam de ficar visíveis, a menos que você os inicialize ou crie links dentro do home do perfil. |
A desvantagem do padrão é que os perfis no host compartilham as mesmas
credenciais e configurações de CLI em nível de usuário, embaixo de ~. Se você precisa de um perfil com
identidade git, chaves SSH, login da CLI do GitHub, configuração de npm ou login de CLI de nuvem
separados, use home_mode: profile e inicialize essas ferramentas dentro do home daquele
perfil, de propósito.
Se você quer mesmo isolamento estrito de configuração de ferramentas por perfil, defina:
terminal:
home_mode: profile
Nesse modo, os subprocessos de ferramenta usam {HERMES_HOME}/home como HOME. O Hermes também
define HERMES_REAL_HOME, para que os scripts ainda consigam localizar o home real do usuário quando
precisarem. Backends de contêiner continuam usando {HERMES_HOME}/home no modo auto,
porque esse diretório fica no volume de dados persistente do Hermes.
Scripts que precisam distinguir o estado do perfil do home real do usuário devem
preferir HERMES_HOME para dados do Hermes e HERMES_REAL_HOME para o home da conta:
from pathlib import Path
import os
hermes_home = Path(os.environ["HERMES_HOME"])
real_home = Path(os.environ.get("HERMES_REAL_HOME", os.environ["HOME"]))
O agente tem o mesmo acesso ao sistema de arquivos que a sua conta de usuário. Use hermes tools para desligar as ferramentas que você não quer, ou mude para o Docker para ter sandbox.
Backend Docker
Roda os comandos dentro de um contêiner Docker com endurecimento de segurança (todas as capacidades removidas, sem escalonamento de privilégio, limites de PID).
Um único contêiner persistente, compartilhado entre processos do Hermes. O Hermes sobe UM contêiner de vida longa no primeiro uso e roteia toda chamada de terminal, de arquivo e de execute_code por um docker exec nesse mesmo contêiner — entre sessões, /new, /reset e subagentes de delegate_task. Mudanças de diretório de trabalho, pacotes instalados, arquivos em /workspace e processos em segundo plano atravessam de uma chamada de ferramenta para a próxima, e de um processo do Hermes para o próximo. Quando você fecha uma sessão da TUI, roda /quit ou inicia uma nova invocação do hermes, o contêiner continua rodando e o próximo processo do Hermes o reaproveita por uma busca via rótulo. Veja Ciclo de vida do contêiner abaixo para as regras exatas de desmontagem.
Modo de isolamento por sessão (container_persistent: false). Definir container_persistent: false no backend Docker muda para um contêiner por sessão: cada conversa (sessão do aplicativo desktop, conversa do gateway, sessão da TUI) ganha o próprio sandbox novo, criado na primeira chamada de terminal/arquivo e removido quando a sessão fecha ou fica ociosa além de lifetime_seconds. Nada atravessa de uma sessão para outra — nem estado de sistema de arquivos, nem montagens, nem processos em segundo plano. Com docker_mount_cwd_to_workspace: true, só o espaço de trabalho anexado àquela sessão é montado em /workspace; uma sessão nova sem diretório anexado recebe um espaço de trabalho vazio em vez de herdar a montagem da sessão anterior. Subagentes de delegate_task continuam compartilhando o contêiner da sessão pai. Use este modo quando o sandbox for uma fronteira de segurança entre conversas; mantenha o padrão true quando você quiser o contêiner compartilhado de vida longa descrito acima.
terminal:
backend: docker
docker_image: "nikolaik/python-nodejs:python3.11-nodejs20"
docker_mount_cwd_to_workspace: false # Monta o diretório de lançamento em /workspace
docker_run_as_host_user: false # Veja "Rodar o contêiner como o usuário do host" abaixo
docker_forward_env: # Variáveis de ambiente do host a repassar para dentro do contêiner
- "GITHUB_TOKEN"
docker_env: # Variáveis de ambiente literais a injetar (KEY=value)
DEBUG: "1"
PYTHONUNBUFFERED: "1"
docker_volumes: # Montagens de diretórios do host
- "/home/user/projects:/workspace/projects"
- "/home/user/data:/data:ro" # :ro para somente leitura
docker_extra_args: # Flags extras acrescentadas literalmente ao `docker run`
- "--gpus=all"
- "--network=host"
docker_network: true # false = isola o contêiner da rede (--network=none)
# Limites de recurso
container_cpu: 1 # Núcleos de CPU (0 = ilimitado)
container_memory: 5120 # MB (0 = ilimitado)
container_disk: 51200 # MB (exige overlay2 em XFS+pquota)
container_persistent: true # true = persiste /workspace + /root, contêiner compartilhado; false = contêiner novo por sessão (veja abaixo)
# Reaproveitamento de contêiner entre processos (os padrões seguem o contrato de
# "um contêiner de vida longa compartilhado entre sessões" — veja Ciclo de vida do contêiner).
docker_persist_across_processes: true # Reaproveita o contêiner entre reinícios do Hermes
docker_shared_container_key: "" # Faz perfis confiáveis compartilharem uma identidade
docker_orphan_reaper: true # Varre contêineres Exited abandonados na inicialização
# Configurações de ciclo de vida comuns a vários backends (valem também para o docker)
timeout: 180 # Tempo limite por comando, em segundos
lifetime_seconds: 300 # Janela do coletor de ociosos; também alimenta o limiar de 2x do coletor de órfãos
docker_env versus docker_forward_env: o primeiro injeta pares literais KEY=value que você especifica na configuração (os valores ficam no seu config.yaml ou são passados como um dicionário JSON via TERMINAL_DOCKER_ENV='{"DEBUG":"1"}'). O segundo repassa valores do seu shell ou do ~/.hermes/.env, então o segredo em si nunca aparece no arquivo de configuração. Use docker_forward_env para tokens e docker_env para ajustes estáticos de que o contêiner precisa.
terminal.docker_extra_args (também pode ser sobrescrito por TERMINAL_DOCKER_EXTRA_ARGS='["--gpus=all"]') permite passar flags arbitrárias do docker run que o Hermes não expõe como chaves de primeira classe — --gpus, --network, --add-host, sobrescritas alternativas de --security-opt etc. Cada item precisa ser uma string; a lista é acrescentada por último à invocação montada do docker run, então pode sobrescrever os padrões do Hermes se preciso. Use com parcimônia — flags que conflitam com o endurecimento do sandbox (remoção de capacidades, --user, a montagem do espaço de trabalho) enfraquecem o isolamento em silêncio.
terminal.docker_network (padrão true; variável de ambiente: TERMINAL_DOCKER_NETWORK) — defina como false para rodar o contêiner de sandbox com --network=none, cortando toda saída de rede dos comandos do agente. Vale para o contêiner de execução usado por terminal, execute_code e as ferramentas de arquivo. Como os contêineres persistem entre processos do Hermes, virar isso para false enquanto existe um contêiner mais antigo com rede vai remover esse contêiner e subir um novo, isolado da rede (um aviso é registrado); processos em segundo plano rodando lá dentro se perdem. Prefira esta chave a passar --network=none pelo docker_extra_args.
Requisitos: Docker Desktop ou Docker Engine instalado e rodando. O Hermes procura no $PATH e também nos locais comuns de instalação do macOS (/usr/local/bin/docker, /opt/homebrew/bin/docker, o pacote do aplicativo Docker Desktop). Podman é suportado de fábrica: defina HERMES_DOCKER_BINARY=podman (ou o caminho completo) para forçá-lo quando os dois estiverem instalados.
Ciclo de vida do contêiner
Todo contêiner gerenciado pelo Hermes recebe três rótulos, para que processos seguintes (e o coletor de órfãos) consigam identificá-lo:
hermes-agent=1— marca como gerenciado pelo Hermeshermes-task-id=<task_id higienizado>— é a chave da checagem de reaproveitamento por tarefahermes-profile=<nome de perfil higienizado>— limita o reaproveitamento e a coleta ao perfil Hermes ativo, por padrão; quandodocker_shared_container_keyestá definido, usa-se o valor higienizado dele
Na inicialização, o Hermes roda docker ps --filter label=hermes-task-id=<id> --filter label=hermes-profile=<identity> e se conecta ao contêiner existente quando encontra um. A identidade é o perfil ativo, a menos que docker_shared_container_key inscreva explicitamente perfis confiáveis num valor comum. Se o contêiner estiver exited (por exemplo, depois que o daemon do Docker reiniciou), ele recebe um docker start e é reaproveitado — o estado do sistema de arquivos e os pacotes instalados sobrevivem, mas os processos em segundo plano dentro do contêiner, não.
Quando um processo do Hermes termina — /quit, fechar uma sessão da TUI, desligar o gateway, até um SIGKILL — o caminho de limpeza não faz nada com o contêiner no modo padrão. O contêiner continua rodando. O próximo processo do Hermes se conecta a ele em milissegundos, pela checagem de rótulo. É esse o comportamento exigido pelo contrato de "um contêiner de vida longa compartilhado entre sessões": é a única forma de processos em segundo plano (watchers do npm, servidores de desenvolvimento, pytest demorado) sobreviverem entre sessões.
O contêiner só é desmontado (parado e removido com docker rm -f) nestes casos:
| Gatilho | Quando dispara |
|---|---|
docker_persist_across_processes: false | Isolamento explícito por processo. Todo cleanup() faz stop + rm -f. Igual ao comportamento anterior à issue #20561. |
Coletor de ociosos (lifetime_seconds, padrão 300s) | Só quando o ambiente está com persist_across_processes=false. Ambientes em modo de persistência não fazem nada; o contêiner sobrevive à varredura de ociosos. |
| Coletor de órfãos na próxima inicialização | Varre contêineres com rótulo hermes no estado Exited mais velhos que 2 × lifetime_seconds (padrão 600s = 10 min), limitado ao perfil atual. Contêineres rodando nunca são tocados — segurança para processos irmãos. Defina docker_orphan_reaper: false para desligar. |
| Ação direta do usuário | docker rm -f, docker system prune, reinício do Docker Desktop. Não definimos --restart=always, então reiniciar o host deixa o contêiner Exited (a camada CoW sobrevive e é reaproveitada na próxima inicialização, mas os processos em segundo plano se foram). |
Casos de borda que vale conhecer:
- OOM kill do PID 1 dentro do contêiner faz o contêiner passar para
Exited. O próximo reaproveitamento vai dardocker startnele; o estado do sistema de arquivos sobrevive, os processos em segundo plano não. - Trocar de perfil isola os contêineres entre si — um contêiner com rótulo
hermes-profile=worké invisível para um processo do Hermes rodando sobhermes-profile=research. O coletor de órfãos também é limitado ao perfil, então contêineres de outro perfil não são coletados por acidente, mas também não são limpos automaticamente até você iniciar o Hermes de novo no perfil original deles. - Compartilhamento explícito entre perfis — defina o mesmo
docker_shared_container_key(não vazio) sobterminal:nos perfis que colaboram de propósito num único espaço de trabalho confiável. Isso substitui só o rótulo de identidade do contêiner; as checagens de compatibilidade de tarefa, de saída e de rede continuam valendo. Perfis sem a chave seguem isolados. O rótulo de identidade é derivado da chave com um sufixo de resumo curto, então chaves parecidas (team/workspaceversusteam_workspace) nunca colidem no mesmo contêiner. Importante: um contêiner compartilhado é criado uma única vez, pelo perfil que o inicia primeiro — odocker_image, os volumes, o tamanho de shm e as outras configurações imutáveis do Docker daquele perfil vencem, e os perfis seguintes se conectam a ele como está; configurações diferentes nos arquivos deles são ignoradas até o contêiner ser removido e recriado. Perfis que compartilham uma chave devem combinar imagem e montagens.
Subagentes em paralelo criados por delegate_task(tasks=[...]) compartilham esse único contêiner — cd concorrente, mudanças de ambiente e escritas no mesmo caminho vão colidir. Se um subagente precisa de um sandbox isolado, ele tem que registrar uma sobrescrita de imagem por tarefa via register_task_env_overrides(), o que ambientes de RL e de benchmark (TerminalBench2, HermesSweEnv etc.) fazem automaticamente para as imagens Docker por tarefa.
Endurecimento de segurança:
--cap-drop ALLcom apenasDAC_OVERRIDE,CHOWNeFOWNERdevolvidos--security-opt no-new-privileges--pids-limit 256- tmpfs com tamanho limitado para
/tmp(512MB),/var/tmp(256MB) e/run(64MB)
Repasse de credenciais: as variáveis de ambiente listadas em docker_forward_env são resolvidas primeiro no ambiente do seu shell, depois no ~/.hermes/.env. Skills também podem declarar required_environment_variables, que são mescladas automaticamente.
Sobrescritas por variável de ambiente
Toda chave sob terminal: tem uma sobrescrita por variável de ambiente no formato TERMINAL_<KEY_UPPERCASE>. As mais úteis para o backend Docker:
| Variável de ambiente | Corresponde a | Observações |
|---|---|---|
TERMINAL_DOCKER_IMAGE | docker_image | Imagem base |
TERMINAL_DOCKER_FORWARD_ENV | docker_forward_env | Array JSON: '["GITHUB_TOKEN","OPENAI_API_KEY"]' |
TERMINAL_DOCKER_ENV | docker_env | Dicionário JSON: '{"DEBUG":"1"}' |
TERMINAL_DOCKER_VOLUMES | docker_volumes | Array JSON de strings "host:container[:ro]" |
TERMINAL_DOCKER_EXTRA_ARGS | docker_extra_args | Array JSON |
TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACE | docker_mount_cwd_to_workspace | true / false |
TERMINAL_DOCKER_RUN_AS_HOST_USER | docker_run_as_host_user | true / false |
TERMINAL_DOCKER_NETWORK | docker_network | true / false — padrão true; false = --network=none |
TERMINAL_DOCKER_PERSIST_ACROSS_PROCESSES | docker_persist_across_processes | true / false — padrão true |
TERMINAL_DOCKER_SHARED_CONTAINER_KEY | docker_shared_container_key | Identidade compartilhada explícita para perfis confiáveis; vazio por padrão |
TERMINAL_DOCKER_ORPHAN_REAPER | docker_orphan_reaper | true / false — padrão true |
TERMINAL_CONTAINER_CPU | container_cpu | Núcleos de CPU |
TERMINAL_CONTAINER_MEMORY | container_memory | MB |
TERMINAL_CONTAINER_DISK | container_disk | MB |
TERMINAL_CONTAINER_PERSISTENT | container_persistent | true / false — controla os diretórios de espaço de trabalho montados por bind, coisa diferente de docker_persist_across_processes |
TERMINAL_LIFETIME_SECONDS | lifetime_seconds | Janela do coletor de ociosos |
TERMINAL_TIMEOUT | timeout | Tempo limite por comando |
HERMES_DOCKER_BINARY | nenhuma | Força um caminho específico do binário docker/podman |
Backend SSH
Roda os comandos num servidor remoto por SSH. Usa ControlMaster para reaproveitar a conexão (keepalive de 5 minutos em ociosidade). O shell persistente vem ligado por padrão — o estado (diretório atual, variáveis de ambiente) sobrevive de um comando para outro.
terminal:
backend: ssh
persistent_shell: true # Mantém uma sessão bash de vida longa (padrão: true)
Variáveis de ambiente obrigatórias:
TERMINAL_SSH_HOST=meu-servidor.exemplo.com
TERMINAL_SSH_USER=ubuntu
Opcionais:
| Variável | Padrão | Descrição |
|---|---|---|
TERMINAL_SSH_PORT | 22 | Porta do SSH |
TERMINAL_SSH_KEY | (padrão do sistema) | Caminho da chave privada SSH |
TERMINAL_SSH_PERSISTENT | true | Liga o shell persistente |
Como funciona: conecta na inicialização com BatchMode=yes e StrictHostKeyChecking=accept-new. O shell persistente mantém um único processo bash -l vivo na máquina remota, comunicando por arquivos temporários. Comandos que precisam de stdin_data ou de sudo caem automaticamente para o modo de tiro único.
Backend Modal
Roda os comandos num sandbox na nuvem da Modal. Cada tarefa recebe uma VM isolada com CPU, memória e disco configuráveis. O sistema de arquivos pode ser salvo em snapshot e restaurado entre sessões.
terminal:
backend: modal
container_cpu: 1 # Núcleos de CPU
container_memory: 5120 # MB (5GB)
container_disk: 51200 # MB (50GB)
container_persistent: true # Snapshot/restauração do sistema de arquivos
Obrigatório: ou as variáveis de ambiente MODAL_TOKEN_ID + MODAL_TOKEN_SECRET, ou um arquivo de configuração ~/.modal.toml.
Persistência: quando ligada, o sistema de arquivos do sandbox recebe um snapshot na limpeza e é restaurado na próxima sessão. Os snapshots são rastreados em ~/.hermes/modal_snapshots.json. Isso preserva o estado do sistema de arquivos, não processos vivos, espaço de PID nem tarefas em segundo plano.
Arquivos de credencial: montados automaticamente a partir de ~/.hermes/ (tokens OAuth etc.) e sincronizados antes de cada comando.
Backend Daytona
Roda os comandos num espaço de trabalho gerenciado da Daytona. Suporta parar/retomar para dar persistência.
terminal:
backend: daytona
container_cpu: 1 # Núcleos de CPU
container_memory: 5120 # MB → convertido para GiB
container_disk: 10240 # MB → convertido para GiB (máximo de 10 GiB)
container_persistent: true # Para/retoma em vez de apagar
Obrigatório: variável de ambiente DAYTONA_API_KEY.
Persistência: quando ligada, os sandboxes são parados (não apagados) na limpeza e retomados na próxima sessão. Os nomes de sandbox seguem o padrão hermes-{task_id}.
Limite de disco: a Daytona impõe um máximo de 10 GiB. Pedidos acima disso são limitados, com um aviso.
Backend Vercel Sandbox
Roda os comandos numa microVM na nuvem do Vercel Sandbox. O Hermes usa as superfícies normais de terminal e de ferramentas de arquivo; não existem ferramentas específicas da Vercel expostas ao modelo.
terminal:
backend: vercel_sandbox
vercel_runtime: node24 # node24 | node22 | python3.13
cwd: /vercel/sandbox # raiz padrão do espaço de trabalho
container_persistent: true # Snapshot/restauração do sistema de arquivos
container_disk: 51200 # Só o padrão compartilhado; disco personalizado não é suportado
Instalação obrigatória: instale o extra opcional do SDK:
pip install 'hermes-agent[vercel]'
Autenticação obrigatória: configure a autenticação por token de acesso com os três valores VERCEL_TOKEN, VERCEL_PROJECT_ID e VERCEL_TEAM_ID. É essa a configuração suportada para implantações e para processos Hermes normais, de vida longa, em Render, Railway, Docker e hosts parecidos.
Para desenvolvimento local pontual, o Hermes também aceita tokens OIDC de curta duração da Vercel:
VERCEL_OIDC_TOKEN="$(vc project token <nome-do-projeto>)" hermes chat
De dentro de um diretório de projeto Vercel já vinculado, você pode omitir o nome do projeto:
VERCEL_OIDC_TOKEN="$(vc project token)" hermes chat
Tokens OIDC têm vida curta e não devem ser usados como o caminho documentado de implantação.
Runtime: terminal.vercel_runtime aceita node24, node22 e python3.13. Se não for definido, o Hermes usa node24.
Persistência: com container_persistent: true, o Hermes tira um snapshot do sistema de arquivos do sandbox durante a limpeza e restaura um sandbox posterior da mesma tarefa a partir desse snapshot. O conteúdo do snapshot pode incluir credenciais, skills e arquivos de cache que o Hermes sincronizou para dentro do sandbox. Isso preserva apenas o estado do sistema de arquivos; não preserva a identidade do sandbox vivo, o espaço de PID, o estado do shell nem processos em segundo plano em execução.
Comandos em segundo plano: terminal(background=true) usa o fluxo genérico de processos em segundo plano não locais do Hermes. Você pode criar, consultar, esperar, ver logs e matar processos pela ferramenta normal de processos enquanto o sandbox estiver vivo. O Hermes não oferece recuperação nativa de processos desanexados da Vercel depois de limpeza ou reinício.
Dimensionamento de disco: o Vercel Sandbox ainda não suporta o botão de recurso container_disk do Hermes. Deixe container_disk sem definir ou no padrão compartilhado 51200; valores diferentes do padrão fazem os diagnósticos e a criação do backend falhar, em vez de serem ignorados em silêncio.
Backend Singularity/Apptainer
Roda os comandos num contêiner Singularity/Apptainer. Feito para clusters de HPC e máquinas compartilhadas onde não existe Docker.
terminal:
backend: singularity
singularity_image: "docker://nikolaik/python-nodejs:python3.11-nodejs20"
container_cpu: 1 # Núcleos de CPU
container_memory: 5120 # MB
container_persistent: true # A camada de escrita persiste entre sessões
Requisitos: binário apptainer ou singularity no $PATH.
Tratamento de imagem: URLs Docker (docker://...) são convertidas automaticamente para arquivos SIF e ficam em cache. Arquivos .sif que já existem são usados direto.
Diretório de rascunho: resolvido nesta ordem: TERMINAL_SCRATCH_DIR → TERMINAL_SANDBOX_DIR/singularity → /scratch/$USER/hermes-agent (convenção de HPC) → ~/.hermes/sandboxes/singularity.
Isolamento: usa --containall --no-home para isolamento completo de namespace, sem montar o diretório home do host.
Problemas comuns dos backends de terminal
Se os comandos de terminal falham na hora ou a ferramenta de terminal aparece como desligada:
- local — nenhum requisito especial. É o padrão mais seguro para começar.
- docker — rode
docker versionpara confirmar que o Docker funciona. Se falhar, arrume o Docker ou rodehermes config set terminal.backend local. - ssh —
TERMINAL_SSH_HOSTeTERMINAL_SSH_USERprecisam estar definidos. O Hermes registra um erro claro se faltar um deles. - modal — precisa da variável de ambiente
MODAL_TOKEN_IDou do~/.modal.toml. Rodehermes doctorpara checar. - daytona — precisa de
DAYTONA_API_KEY. O SDK da Daytona cuida da configuração da URL do servidor. - singularity — precisa de
apptainerousingularityno$PATH. Comum em clusters de HPC.
Na dúvida, volte terminal.backend para local e confirme primeiro que os comandos rodam lá.
Sincronização de estado do remoto para o host na desmontagem
Nos backends ssh, modal e daytona, o Hermes empurra o seu estado de ~/.hermes/ (arquivos de credencial, skills, cache) para dentro do sandbox remoto durante a sessão e, na desmontagem, sincroniza de volta os arquivos de estado que mudaram para os lugares originais no host. Arquivos diferentes do que foi enviado originalmente (comparados por hash de conteúdo) são aplicados de volta no lugar; arquivos novos no remoto dentro de um diretório sincronizado (por exemplo, uma skill que o agente criou remotamente) são mapeados de volta para o caminho correspondente no host. Arquivos de credencial que só são enviados nunca são sobrescritos no host.
- A sincronização de volta tenta até 3 vezes, com espera crescente, e recusa extrair arquivos compactados remotos maiores que 2 GiB.
- Docker e Singularity usam montagens por bind (visão viva do sistema de arquivos do host) e não precisam disso.
- Isso cobre o estado do Hermes (
~/.hermes/), não arquivos quaisquer da árvore de trabalho dentro do sandbox — peça ao agente para copiar explicitamente os artefatos importantes para fora (por exemplo, comscp,modal volume put) antes de o sandbox ser destruído.
Montagens de volume do Docker
Com o backend Docker, docker_volumes deixa você compartilhar diretórios do host com o contêiner. Cada item usa a sintaxe padrão do -v do Docker: caminho_no_host:caminho_no_contêiner[:opções].
terminal:
backend: docker
docker_volumes:
- "/home/user/projects:/workspace/projects" # Leitura e escrita (padrão)
- "/home/user/datasets:/data:ro" # Somente leitura
- "/home/user/.hermes/cache/documents:/output" # Exportações visíveis ao gateway
Isso é útil para:
- Entregar arquivos ao agente (conjuntos de dados, configurações, código de referência)
- Receber arquivos do agente (código gerado, relatórios, exportações)
- Espaços de trabalho compartilhados, em que você e o agente acessam os mesmos arquivos
Se você usa um gateway de mensagens e quer que o agente envie arquivos gerados por
MEDIA:/..., prefira uma montagem dedicada de exportação visível no host, como
/home/user/.hermes/cache/documents:/output.
- Escreva os arquivos, dentro do Docker, em
/output/... - Emita o caminho do host no
MEDIA:, por exemplo:MEDIA:/home/user/.hermes/cache/documents/report.txt - Não emita
/workspace/...nem/output/...a menos que esse caminho exato também exista para o processo do gateway no host
Chaves duplicadas em YAML sobrescrevem as anteriores em silêncio. Se você já tem um
bloco docker_volumes:, junte as novas montagens na mesma lista em vez de acrescentar
outra chave docker_volumes: mais adiante no arquivo.
Também pode ser definido por variável de ambiente: TERMINAL_DOCKER_VOLUMES='["/host:/container"]' (array JSON).
Repasse de credenciais no Docker
Por padrão, as sessões de terminal do Docker não herdam credenciais quaisquer do host. Se você precisa de um token específico dentro do contêiner, acrescente-o a terminal.docker_forward_env.
terminal:
backend: docker
docker_forward_env:
- "GITHUB_TOKEN"
- "NPM_TOKEN"
O Hermes resolve cada variável listada primeiro no seu shell atual e, se não achar, cai para o ~/.hermes/.env, caso ela tenha sido salva com hermes config set.
Tudo que está listado em docker_forward_env fica visível para os comandos rodados dentro do contêiner. Repasse só credenciais que você aceita expor à sessão de terminal.
Rodar o contêiner como o seu usuário do host
Por padrão, contêineres Docker rodam como root (UID 0). Arquivos criados dentro de /workspace ou de outras montagens por bind acabam pertencendo ao root no host, então depois de uma sessão você precisa de um sudo chown antes de conseguir editá-los no seu editor do host. A flag terminal.docker_run_as_host_user resolve isso:
terminal:
backend: docker
docker_run_as_host_user: true # padrão: false
Quando ligada, o Hermes acrescenta --user $(id -u):$(id -g) ao comando docker run, para que os arquivos escritos nos diretórios montados por bind (/workspace, /root, tudo que está em docker_volumes) pertençam ao seu usuário do host, não ao root. A contrapartida: o contêiner não consegue mais fazer apt install nem escrever em caminhos do root, como /root/.npm — use uma imagem base cujo HOME pertença a um usuário sem privilégio (ou acrescente as ferramentas de que precisa na construção da imagem) se você precisa das duas coisas.
Deixe em false (o padrão) para manter o comportamento compatível com o de antes. Ligue quando o seu fluxo for basicamente "editar arquivos montados do host" e você estiver de saco cheio de sudo chown -R.
Opcional: montar o diretório de lançamento em /workspace
Sandboxes Docker ficam isolados por padrão. O Hermes não passa o seu diretório de trabalho atual do host para dentro do contêiner, a menos que você peça explicitamente.
Ligue no config.yaml:
terminal:
backend: docker
docker_mount_cwd_to_workspace: true
Quando ligado:
- se você lança o Hermes de
~/projects/meu-app, esse diretório do host é montado por bind em/workspace - o backend Docker começa em
/workspace - as ferramentas de arquivo e os comandos de terminal veem o mesmo projeto montado
Quando desligado, /workspace continua pertencendo ao sandbox, a menos que você monte algo explicitamente por docker_volumes.
Contrapartida de segurança:
falsepreserva a fronteira do sandboxtruedá ao sandbox acesso direto ao diretório de onde você lançou o Hermes
Use essa opção só quando você quiser de propósito que o contêiner trabalhe em arquivos vivos do host.
Shell persistente
Por padrão, cada comando de terminal roda no próprio subprocesso — diretório de trabalho, variáveis de ambiente e variáveis de shell voltam ao início entre comandos. Quando o shell persistente está ligado, um único processo bash de vida longa é mantido vivo entre as chamadas de execute(), para que o estado sobreviva de um comando para outro.
Isso é mais útil no backend SSH, onde também elimina o custo de conexão a cada comando. O shell persistente vem ligado por padrão no SSH e desligado no backend local.
terminal:
persistent_shell: true # padrão — liga o shell persistente para SSH
Para desligar:
hermes config set terminal.persistent_shell false
O que sobrevive entre comandos:
- Diretório de trabalho (
cd /tmpcontinua valendo no comando seguinte) - Variáveis de ambiente exportadas (
export FOO=bar) - Variáveis de shell (
MY_VAR=hello)
Precedência:
| Nível | Variável | Padrão |
|---|---|---|
| Configuração | terminal.persistent_shell | true |
| Sobrescrita do SSH | TERMINAL_SSH_PERSISTENT | segue a configuração |
| Sobrescrita do local | TERMINAL_LOCAL_PERSISTENT | false |
As variáveis de ambiente por backend têm a maior precedência. Se você quer shell persistente também no backend local:
export TERMINAL_LOCAL_PERSISTENT=true
Comandos que precisam de stdin_data ou de sudo caem automaticamente para o modo de tiro único, já que a entrada padrão do shell persistente está ocupada pelo protocolo de IPC.
Veja Execução de código e a seção de Terminal do README para os detalhes de cada backend.
Configurações de skill
Skills podem declarar as próprias configurações no frontmatter do SKILL.md. São valores que não são segredo (caminhos, preferências, configurações de domínio), guardados no espaço de nomes skills.config do config.yaml.
skills:
config:
myplugin:
path: ~/myplugin-data # Exemplo — cada skill define as próprias chaves
Como funcionam as configurações de skill:
hermes config migratevarre todas as skills ligadas, encontra as configurações não preenchidas e se oferece para perguntar a vocêhermes config showmostra todas as configurações de skill sob "Skill Settings", junto com a skill a que pertencem- Quando uma skill carrega, os valores de configuração já resolvidos são injetados automaticamente no contexto dela
Definir valores na mão:
hermes config set skills.config.myplugin.path ~/myplugin-data
Para saber como declarar configurações nas suas próprias skills, veja Criar skills — configurações.
Guarda sobre escritas de skill feitas pelo agente
Quando o agente usa skill_manage para criar, editar, corrigir ou apagar uma skill, o Hermes pode, opcionalmente, varrer o conteúdo novo/atualizado buscando padrões de palavras-chave perigosas (coleta de credenciais, injeção de prompt óbvia, instruções de exfiltração). O varredor vem desligado por padrão — fluxos de agente reais que mexem legitimamente em ~/.ssh/ ou citam $OPENAI_API_KEY disparavam a heurística com frequência demais. Ligue de novo se você quiser que o varredor pergunte antes de as escritas de skill do agente entrarem:
skills:
guard_agent_created: true # padrão: false
Quando ligado, toda escrita de skill_manage sinalizada aparece como um pedido de aprovação, com a justificativa do varredor. As escritas aceitas entram; as negadas devolvem ao agente um erro explicativo.
Aprovação de escritas de skill
Independentemente do varredor de conteúdo acima, skills.write_approval coloca toda escrita de skill do agente (criar / editar / corrigir / apagar / arquivos de apoio) atrás da sua aprovação explícita — o mesmo mecanismo de aprovar/negar dos comandos perigosos:
skills:
write_approval: false # false = escreve livremente (padrão) | true = deixa toda escrita em espera para revisão
Quando ligado, as escritas de skill ficam em espera sob ~/.hermes/pending/skills/ e são revisadas com /skills pending, /skills diff <id>, /skills approve <id> e /skills reject <id> — pela CLI ou por qualquer plataforma de mensagens. Alterne em tempo de execução com /skills approval on|off. A memória tem a mesma guarda (memory.write_approval, abaixo). Passo a passo completo: Barrar escritas de skill do agente.
Configuração de memória
memory:
memory_enabled: true
user_profile_enabled: true
memory_char_limit: 2200 # ~800 tokens
user_char_limit: 1375 # ~500 tokens
write_approval: false # true = exige aprovação antes de qualquer escrita de memória
Com memory.write_approval: true, as escritas de memória precisam da sua aprovação antes de entrar: turnos interativos na CLI perguntam na hora; sessões de mensagens e a revisão de autoaperfeiçoamento em segundo plano deixam a escrita em espera para revisão com /memory pending → /memory approve <id> / /memory reject <id>. Alterne em tempo de execução com /memory approval on|off. Veja Controlar as escritas de memória.
Truncamento de arquivos de contexto
Controla quanto conteúdo o Hermes carrega de cada arquivo de contexto automático antes de aplicar o truncamento de começo/fim. Vale para os arquivos injetados no prompt de sistema, como SOUL.md, .hermes.md, AGENTS.md, CLAUDE.md e .cursorrules. Não afeta a ferramenta read_file.
context_file_max_chars: null # padrão — teto dinâmico, proporcional à janela de contexto do modelo (piso de 20 mil, teto de 500 mil caracteres)
Defina um inteiro positivo para fixar um teto em vez do comportamento dinâmico:
context_file_max_chars: 25000
Segurança na leitura de arquivos
Controla quanto conteúdo uma única chamada de read_file pode devolver. Leituras que passam do limite são recusadas com um erro dizendo ao agente para usar offset e limit e pegar um trecho menor. Isso evita que uma única leitura de um pacote JS minificado ou de um arquivo de dados grande inunde a janela de contexto.
file_read_max_chars: 100000 # padrão — cerca de 25 a 35 mil tokens
Aumente se você está num modelo com janela de contexto grande e lê arquivos grandes com frequência. Diminua para modelos de contexto pequeno, para manter as leituras eficientes:
# Modelo de contexto grande (200 mil ou mais)
file_read_max_chars: 200000
# Modelo local pequeno (contexto de 16 mil)
file_read_max_chars: 30000
O agente também elimina leituras de arquivo repetidas automaticamente — se a mesma região de um arquivo é lida duas vezes e o arquivo não mudou, um marcador leve é devolvido em vez de reenviar o conteúdo. Isso é zerado na compressão de contexto, para que o agente possa reler os arquivos depois que o conteúdo deles foi resumido e sumiu.
Limites de truncamento de saída de ferramenta
Três tetos relacionados controlam quanta saída bruta uma ferramenta pode devolver antes de o Hermes truncar:
tool_output:
max_bytes: 50000 # teto da saída do terminal (caracteres)
max_lines: 2000 # teto de paginação do read_file
max_line_length: 2000 # teto por linha na visão numerada do read_file
max_bytes— quando um comandoterminalproduz mais que essa quantidade de caracteres somando stdout e stderr, o Hermes guarda os primeiros 40% e os últimos 60% e coloca um aviso[OUTPUT TRUNCATED]no meio. Padrão50000(algo em torno de 12 a 15 mil tokens nos tokenizadores típicos).max_lines— limite superior do parâmetrolimitde uma única chamada deread_file. Pedidos acima disso são cortados, para que uma única leitura não inunde a janela de contexto. Padrão2000.max_line_length— teto por linha aplicado quando oread_filemonta a visão numerada. Linhas mais longas que isso são cortadas nessa quantidade de caracteres, seguidas de... [truncated]. Padrão2000.
Aumente os limites em modelos com janela de contexto grande, que aguentam mais saída bruta por chamada. Diminua em modelos de contexto pequeno, para manter os resultados de ferramenta compactos:
# Modelo de contexto grande (200 mil ou mais)
tool_output:
max_bytes: 150000
max_lines: 5000
# Modelo local pequeno (contexto de 16 mil)
tool_output:
max_bytes: 20000
max_lines: 500
Orçamento de transbordo de resultado de ferramenta
Separado do truncamento, resultados de ferramenta grandes demais transbordam para o disco em vez de serem cortados: a saída completa é salva em $HERMES_HOME/cache/spillover/ e o conteúdo que fica no contexto é substituído por uma prévia mais o caminho do arquivo salvo (que dá para ler com read_file usando offset/limit, ou processar com execute_code). O limiar genérico de transbordo por resultado é de 100.000 caracteres, reduzido automaticamente em modelos de contexto pequeno.
Resultados de ferramentas MCP (ferramentas com nome mcp_*) transbordam num limiar mais apertado, de 50.000 caracteres, por padrão: servidores MCP costumam devolver cargas grandes e sem paginação (catálogos de descoberta de ferramentas, execuções em lote) que, de outra forma, ficariam abaixo do limiar genérico e inchariam o contexto em todo turno seguinte. Nada se perde — o resultado completo fica preservado no disco. Sobrescreva o limiar com:
tool_budget:
mcp_result_size_chars: 50000 # limiar de transbordo por resultado para ferramentas mcp_*
O limiar do MCP é sempre limitado pelo limiar genérico por resultado (que pode ter sido reduzido pelo contexto), então aumentá-lo não passa do que a janela do modelo ativo permite.
O Hermes também sinaliza cortes feitos pelo provedor: quando um resultado de ferramenta MCP ou de web traz os próprios marcadores de truncamento (...N more items, "has_more": true, notas de "saved to sandbox"), um aviso de uma linha é acrescentado ao resultado alertando que os dados visíveis estão incompletos e que é preciso paginar/buscar antes de tratar qualquer enumeração como completa.
Desligar conjuntos de ferramentas globalmente
Para suprimir conjuntos de ferramentas específicos na CLI e em toda plataforma de gateway de uma vez só,
liste os nomes deles em agent.disabled_toolsets:
agent:
disabled_toolsets:
- memory # esconde as ferramentas de memória + a injeção de MEMORY_GUIDANCE
- web # nada de web_search / web_extract em lugar nenhum
Isso é aplicado depois da configuração de ferramentas por plataforma (platform_toolsets, escrita pelo
hermes tools), então um conjunto listado aqui é sempre removido — mesmo que a configuração
salva de uma plataforma ainda o liste. Use quando você quiser um único
interruptor de "desliga X em todo lugar" em vez de editar mais de 15 linhas de plataforma na
interface do hermes tools.
Deixar a lista vazia, ou omitir a chave, não faz nada.
Isolamento por worktree do git
Ligue worktrees isolados do git para rodar vários agentes em paralelo no mesmo repositório:
worktree: true # Sempre cria um worktree (igual a hermes -w)
# worktree: false # Padrão — só quando a flag -w é passada
Quando ligado, cada sessão da CLI cria um worktree novo em .worktrees/, com o próprio branch. Os agentes podem editar arquivos, fazer commit, dar push e abrir PRs sem atrapalhar uns aos outros. Worktrees limpos são removidos na saída; os sujos são mantidos para recuperação manual.
Por padrão, o novo worktree parte da ponta do remoto recém-buscada (o upstream do branch atual ou, na falta dele, o branch padrão do remoto), para começar em dia com o projeto em vez de partir do HEAD possivelmente atrasado do clone local. Isso mantém o diff de um PR restrito à mudança real, sem herdar o atraso do clone local. Defina worktree_sync: false para partir do HEAD local — útil offline, ou quando você quer de propósito o estado exato atual do clone como base. Se o remoto não puder ser alcançado, ele cai automaticamente para o HEAD local.
worktree_sync: true # Padrão — parte da ponta buscada do remoto
# worktree_sync: false # Parte do HEAD local (offline / base fixa)
Você também pode listar arquivos ignorados pelo git para copiar para dentro dos worktrees, com um .worktreeinclude na raiz do repositório:
# .worktreeinclude
.env
.venv/
node_modules/
Compressão de contexto
O Hermes comprime automaticamente conversas longas para caber na janela de contexto do seu modelo. O resumidor da compressão é uma chamada de LLM separada — você pode apontá-lo para qualquer provedor ou endpoint.
Todas as configurações de compressão ficam no config.yaml (nada de variáveis de ambiente).
Referência completa
compression:
enabled: true # Liga/desliga a compressão
progress_notices: false # Opcional: entrega os avisos rotineiros de progresso da compressão às plataformas de chat — veja abaixo
threshold: 0.50 # Comprime nesta % do limite de contexto
threshold_tokens: null # Teto absoluto de tokens (opcional) — vale o menor entre a proporção e o absoluto
target_ratio: 0.20 # Fração do limiar a preservar como cauda recente
tail_mode: lean # Retenção de cauda: "lean" (padrão — cauda limitada a 2,5%, de 10 mil a 25 mil, com resumos + índice de âncoras + ponteiros de recuperação via session_search no sumário; cerca de 3x menos tokens retidos depois da compactação) ou "legacy" (cauda literal de 0,20 × limiar)
protect_last_n: 20 # Mínimo de mensagens recentes a manter sem comprimir
protect_first_n: 3 # Mensagens iniciais fora do sistema fixadas entre compactações (0 = não fixa nada)
in_place: true # Compacta no mesmo id de sessão (sem rotação) — veja abaixo
idle_compact_after_seconds: 0 # Compactação por ociosidade, opcional (0 = desligado) — veja abaixo
hygiene_hard_message_limit: 5000 # Válvula de segurança do gateway — veja abaixo
hygiene_timeout_seconds: 30 # Máximo de segundos SEM saída do modelo de resumo antes de a compressão de higiene ser cortada
hygiene_total_ceiling_seconds: 600 # Teto absoluto da espera de higiene, mesmo com tokens ainda chegando
hygiene_failure_cooldown_seconds: 300 # Primeiro degrau da espera crescente por falha de higiene, por sessão (x1/x3/x9, com teto de 1h)
context_timeout_seconds: 120 # Orçamento de inatividade do compress_context dentro do agente (laço /compress / pré-checagem) — veja abaixo
context_total_ceiling_seconds: 600 # Teto absoluto da espera *pré-commit* do compress_context dentro do agente, mesmo com tokens ainda chegando (um commit do SessionDB já iniciado nunca é abandonado; estouros são registrados e mostrados)
proactive_prune_tokens: 0 # Gatilho, em tokens, da poda de resultados de ferramenta sem LLM, opcional (0 = desligado; veja abaixo)
proactive_prune_min_result_chars: 8000 # A passada de resumo da poda só mexe em resultados de ferramenta maiores que isso (limitado a >= 200)
proactive_prune_min_reclaim_tokens: 4096 # A poda só efetiva quando recupera pelo menos esta quantidade de tokens (0 = efetiva qualquer uma)
# O modelo/provedor de resumo é configurado sob auxiliary:
auxiliary:
compression:
model: "" # Vazio = usa o modelo principal de chat. Sobrescreva com, por exemplo, "google/gemini-3-flash-preview" para uma compressão mais barata/rápida.
provider: "auto" # Provedor: "auto", "openrouter", "nous", "codex", "main" etc.
base_url: null # Endpoint personalizado compatível com OpenAI (sobrescreve o provedor)
Configurações mais velhas, com compression.summary_model, compression.summary_provider e compression.summary_base_url, são migradas automaticamente para auxiliary.compression.* na primeira carga (versão 17 da configuração). Não precisa fazer nada na mão.
progress_notices (padrão false) controla se os status rotineiros de progresso da compressão chegam às plataformas de chat (Telegram, Discord, Slack etc.). Por decisão de projeto, a compressão automática é silenciosa nas superfícies de chat — roda em segundo plano, com registro apenas no servidor. Defina progress_notices: true para optar por ver o ciclo de vida rotineiro nas plataformas de chat: o aviso de início "Compacting context…", os gatilhos de compressão de pré-checagem/pré-API, a compactação por ociosidade, o progresso de nova tentativa ("Compressed 30 → 12 messages, retrying…") e o aviso "Context compaction complete". A trava vale só para os status de compressão — o barulho operacional não relacionado (falhas de modelo auxiliar, conversa de limite de taxa/nova tentativa de provedor) continua suprimido de qualquer jeito. Avisos de falha de compressão e o retorno do /compress manual sempre aparecem, independentemente desta configuração. Editar este valor num gateway em execução passa a valer na próxima mensagem.
hygiene_hard_message_limit é uma válvula de segurança pré-compressão exclusiva do gateway. Existe para quebrar uma espiral da morte: quando as chamadas de API ficam caindo numa sessão grande demais, o gateway nunca recebe os dados de uso de token, então o limiar baseado em token não dispara, então a transcrição continua crescendo e as quedas pioram. Esse piso baseado em contagem dispara só pela quantidade de mensagens (sempre conhecida, mesmo com falhas de API) para forçar a compressão e recuperar a sessão. Padrão 5000 — muito acima de qualquer sessão normal, inclusive modelos de contexto grande (1 milhão ou mais) fazendo milhares de turnos curtos, que comprimem pelo limiar de token muito antes disso. Aumente para plataformas fora do comum, diminua para forçar uma compressão mais agressiva. Editar este valor num gateway em execução passa a valer na próxima mensagem (veja abaixo).
hygiene_timeout_seconds é o orçamento de inatividade do gateway para essa passada de compressão anterior ao agente — não é um teto de tempo total. A chamada de resumo da compressão vem em streaming do modelo, e cada token que chega conta como progresso: um modelo de raciocínio lento que ainda está gerando vai estendendo o próprio prazo, então modelos de resumo lentos, mas saudáveis, nunca são cortados no meio da geração. Só quando o modelo de resumo fica sem produzir saída por essa quantidade de segundos (backend fora do ar, conexão travada, provedor mudo) é que o gateway avisa o usuário, segue com a mensagem que chegou sem comprimir e registra uma espera temporária de falha por sessão, em vez de parecer travado.
hygiene_total_ceiling_seconds (padrão 600) limita a espera total mesmo com tokens ainda se movendo, para que um fluxo degenerado, gota a gota, não segure um turno para sempre. É limitado a, no mínimo, hygiene_timeout_seconds.
hygiene_failure_cooldown_seconds controla essa espera por sessão depois de um tempo esgotado ou de um aborto na compressão de higiene. Durante a espera, o gateway pula tentativas repetidas de higiene para a mesma sessão grande demais, para que nem toda mensagem que chega trave no mesmo backend auxiliar quebrado. /compress, /reset ou um turno saudável mais tarde ainda conseguem recuperar a sessão.
O valor é o primeiro degrau de uma escada crescente, não um intervalo fixo: falhas consecutivas na mesma sessão esperam 1x, 3x e depois 9x esse valor, com teto de uma hora. Uma sessão cujo modelo de resumo está quebrado de vez, portanto, recua em vez de tentar para sempre num intervalo fixo, e uma execução que realmente encolhe a transcrição volta ao primeiro degrau. A escalada é por sessão e local ao processo — reiniciar o gateway volta ao primeiro degrau, enquanto o prazo da espera em si sobrevive.
context_timeout_seconds (padrão 120) é o mesmo orçamento de inatividade para o compress_context dentro do agente — o laço de conversa, a compactação de pré-checagem e o /compress manual — para que um modelo de resumo travado não consiga parar uma sessão indefinidamente. Tokens de resumo em streaming estendem a espera; só um trabalhador mudo é cortado. Ao estourar o tempo, o Hermes tenta o resumo mais uma vez contra a primeira entrada de auxiliary.compression.fallback_chain (usando o timeout próprio dessa entrada, quando ela declara um) — uma rota travada nunca levanta exceção, então o tratamento de reserva do próprio cliente auxiliar não consegue enxergá-la. Só se essa tentativa também falhar, ou se não houver cadeia de reserva configurada, o Hermes pula a compactação, mantém as mensagens existentes e avisa o usuário. Defina 0 para desligar. A higiene de sessão do gateway mantém o próprio caminho de hygiene_timeout_seconds e não é embrulhada duas vezes.
context_total_ceiling_seconds (padrão 600) limita a espera pré-commit dentro do agente (fase de resumo / streaming) mesmo com tokens ainda se movendo. É limitado a, no mínimo, context_timeout_seconds. A garantia exata: a fase de resumo é limitada por esse teto; a fase de commit é registrada e mostrada se passar dele. Depois que o trabalhador entrou na cerca de commit da compressão e a mutação do SessionDB está em curso, o commit nunca é abandonado no meio — isso arriscaria divergir a transcrição —, mas a espera deixa de ser silenciosa: se o commit passar do teto, o Hermes registra o estouro (WARNING, escalando para ERROR se repetir), manda um aviso único pelo canal de avisos visível ao usuário e continua esperando em incrementos limitados até o commit terminar.
protect_first_n controla quantas mensagens iniciais fora do sistema ficam fixadas em toda compactação. Padrão 3 — a troca inicial de usuário/assistente sobrevive a toda passada do resumidor, para que o objetivo original continue visível. Em sessões de compactação contínua e vida longa, onde o turno inicial já não importa, defina protect_first_n: 0 para não fixar nada além do prompt de sistema + resumo + cauda. O prompt de sistema em si é sempre preservado, independentemente desta configuração.
in_place (padrão true) controla o que acontece com a identidade da sessão quando a compactação dispara. Com true, a compactação reescreve a lista de mensagens e reconstrói o prompt de sistema sem rotacionar o id da sessão — a conversa mantém um id durável a vida inteira (sem cadeia de parent_session_id, sem renumeração nome #2 / #3 nas listas de sessão). A compactação não é destrutiva: o contexto vivo é compactado, mas os turnos anteriores à compactação são arquivados de forma suave sob o mesmo id (marcados como inativos/compactados) — ainda dá para buscar com session_search e recuperar; não foram apagados. Os hooks veem o modo pelo campo in_place no evento session:compress. Defina in_place: false para voltar ao comportamento antigo, em que cada compactação rotaciona para um novo id de sessão ligado ao antigo.
threshold_tokens define um teto absoluto de tokens opcional para o gatilho da compressão. Quando definido, a compressão dispara no menor valor entre o threshold proporcional e essa contagem absoluta — então a compressão nunca dispara depois do número de tokens que o usuário prefere, seja qual for o modelo ativo. Isso resolve o problema de trocar entre modelos com janelas de contexto diferentes (por exemplo, 1 milhão → 400 mil), o que desloca o ponto absoluto de disparo. O teto é limitado ao tamanho de contexto do modelo, então defini-lo acima do que o modelo suporta é seguro — nesse caso, vale o limiar proporcional. Padrão null (desligado — só o limiar proporcional). O teto sobrevive a trocas de modelo e a ativações de reserva.
idle_compact_after_seconds é um gatilho opcional, baseado em tempo, que complementa o threshold baseado em tamanho. Padrão 0 (desligado). Quando definido acima de 0, uma sessão que volta depois de pelo menos essa quantidade de segundos de inatividade compacta o histórico acumulado logo de cara, antes da primeira resposta — assim, uma conversa de vida longa (por exemplo, um papo no Telegram ao qual você volta horas depois) não relê o contexto velho inteiro em todo turno seguinte. Nunca dispara quando o contexto já está no alvo pós-compressão ou abaixo dele (threshold × target_ratio), e respeita as mesmas guardas de espera por falha, de antidebulha e de trava por sessão de toda compactação automática. Exemplo: idle_compact_after_seconds: 1800 compacta depois de 30 minutos parado.
proactive_prune_tokens liga uma poda determinística e sem LLM das cargas antigas de resultado de ferramenta, que roda independentemente do threshold. Em modelos de janela grande, a compactação por threshold (cerca de 50% da janela) raramente dispara, então saídas volumosas de ferramenta (despejos de terminal, leituras de arquivo, extrações de web) pegam carona no histórico e são reenviadas em todo turno seguinte. Quando o histórico reenviado passa de proactive_prune_tokens (padrão 0 = desligado; tente 48000 para ligar), a poda tira resultados idênticos duplicados, resume os mais antigos que estão grandes demais e corta argumentos grandes de chamada de ferramenta — protegendo as protect_last_n mensagens mais recentes e sem nunca chamar o modelo. As saídas completas continuam recuperáveis do armazenamento de sessão. proactive_prune_min_result_chars (padrão 8000, limitado a ≥ 200) define o tamanho abaixo do qual um resultado de ferramenta fica intacto. proactive_prune_min_reclaim_tokens (padrão 4096) impede que uma poda seja efetivada a menos que recupere pelo menos essa quantidade de tokens — uma poda efetivada reescreve histórico já enviado e invalida o prefixo de cache de prompt do provedor, então essa trava mantém essas quebras de cache episódicas e amortizadas (uma quebra significativa, como uma fronteira de compressão) em vez de dispararem a cada iteração de ferramenta. Isso só roda no motor compressor embutido; outros motores de contexto herdam um comportamento vazio.
Nas versões recentes, editar model.context_length ou qualquer chave compression.* no config.yaml de um gateway em execução passa a valer na próxima mensagem — sem reiniciar o gateway, sem /reset e sem rotação de sessão. A assinatura do agente em cache inclui essas chaves, então o gateway reconstrói o agente de forma transparente quando vê uma mudança. Chaves de API e configuração de ferramenta/skill ainda exigem os caminhos usuais de recarga.
Configurações comuns
Padrão (detecção automática) — não precisa configurar nada:
compression:
enabled: true
threshold: 0.50
Usa o seu provedor principal e o seu modelo principal. Sobrescreva por tarefa (por exemplo, auxiliary.compression.provider: openrouter + model: google/gemini-2.5-flash) se você quer a compressão num modelo mais barato que o seu modelo principal de chat.
Forçar um provedor específico (por OAuth ou por chave de API):
auxiliary:
compression:
provider: nous
model: gemini-3-flash
Funciona com qualquer provedor: nous, openrouter, codex, anthropic, main etc.
Endpoint personalizado (auto-hospedado, Ollama, zai, DeepSeek etc.):
auxiliary:
compression:
model: glm-4.7
base_url: https://api.z.ai/api/coding/paas/v4
Aponta para um endpoint personalizado compatível com OpenAI. Usa OPENAI_API_KEY para autenticar.
Como os três botões interagem
auxiliary.compression.provider | auxiliary.compression.base_url | Resultado |
|---|---|---|
auto (padrão) | não definido | Detecta sozinho o melhor provedor disponível |
nous / openrouter / etc. | não definido | Força aquele provedor, usa a autenticação dele |
| qualquer um | definido | Usa o endpoint personalizado direto (o provedor é ignorado) |
O modelo de resumo precisa ter uma janela de contexto pelo menos do tamanho da do seu modelo principal de agente. O compressor manda a seção do meio inteira da conversa para o modelo de resumo — se a janela de contexto dele for menor que a do modelo principal, a chamada de resumo vai falhar com um erro de tamanho de contexto. Quando isso acontece, os turnos do meio são descartados sem resumo, perdendo o contexto da conversa em silêncio. Se você sobrescrever o modelo, confira que o tamanho de contexto dele é igual ou maior que o do seu modelo principal.
Tempo limite do arrendamento de turno do gateway
O gateway serializa os turnos pelo ID de sessão já resolvido, para que duas chaves de roteamento não consigam carregar e escrever a mesma transcrição ao mesmo tempo. Configure a espera máxima pelo arrendamento de forma independente do tempo limite comum de inatividade do agente:
agent:
gateway_turn_lease_timeout: 1800
Se outro turno ainda estiver com o arrendamento da sessão quando esse orçamento acabar, o Hermes falha de forma fechada: não carrega a transcrição nem roda o modelo para a mensagem em espera. O usuário recebe um aviso de recusa e precisa mandar de novo. O Hermes não recoloca a mensagem na fila automaticamente porque fazer isso sem ordenação durável e idempotência poderia processá-la duas vezes. Valores não positivos usam o padrão de 1800 segundos.
Vigia de sessão travada
O gateway roda um vigia de travamento que apenas notifica (agent.session_stall_timeout, padrão 300 segundos, 0 = desligado). Quando uma sessão ocupada tem uma mensagem de entrada em espera e o relógio compartilhado de atividade do agente está parado há pelo menos esse tempo, o gateway registra um WARNING e manda ao usuário uma notificação única:
⚠️ Agent session appears stalled (last activity N min ago). Try /new to reset.
Semântica:
- Só notifica. O vigia nunca mata o turno — diferente de
agent.gateway_timeout, que cancela uma execução depois de inatividade prolongada. O aviso de travamento só te conta que o agente parece emperrado, para você decidir (/new,/stopou continuar esperando). - Uma notificação por episódio de travamento. A trava é liberada quando a entrada em espera é consumida ou a atividade volta, então uma sessão que se recupera e trava de novo notifica de novo.
- O progresso vem só do instantâneo compartilhado de atividade (chamadas de ferramenta, progresso do fluxo da API, batimentos de compressão). A entrada em espera é uma trava de notificação, não um relógio de progresso.
agent:
session_stall_timeout: 300 # segundos; 0 desliga o vigia
Escalada de atenção na reconexão
Quando um adaptador de plataforma não consegue conectar (queda de rede, token de bot revogado, sidecar quebrado), o gateway tenta de novo indefinidamente, com espera exponencial limitada — as tentativas nunca param, então uma queda passageira sempre se resolve sozinha, sem ação do operador. A desvantagem é que uma falha permanente (um token do Telegram revogado, intents privilegiados do Discord faltando) fica igualzinha a um soluço: "tentando de novo", para sempre.
Dois mecanismos tornam as falhas permanentes visíveis:
- Classificação terminal. Falhas cujo tipo de exceção prova que elas nunca vão se resolver sozinhas — tokens rejeitados/revogados (
telegram_auth_error,discord_auth_error,email_auth_error), intents privilegiados faltando (discord_intents_required), um sidecar do Photon cujas dependências não conseguem instalar (SIDECAR_DEPS_MISSING) ou cujo binário do node está faltando (SIDECAR_NODE_MISSING) — são marcadas como fatais em vez de entrar na fila de novas tentativas. A classificação é estritamente por tipo; erros ambíguos sempre continuam tentando. - Escalada de "precisa de atenção". Uma plataforma que fica na fila de tentativas por mais tempo que
agent.reconnect_attention_after(padrão7200segundos = 2 horas,0desliga) recebeneeds_attention: truee uma marca de temporetrying_sinceno status de execução do gateway (hermes status), além de um log de WARNING. As tentativas seguem iguais — isso é um sinal, não um disjuntor. A marca é limpa numa reconexão bem-sucedida.
agent:
reconnect_attention_after: 7200 # segundos; 0 desliga a marca de escalada
Cache de agentes do gateway
O gateway mantém um agente por sessão, para que uma conversa reaproveite o prefixo de prompt em cache em vez de reconstruir o prompt de sistema a cada turno. Esse agente em cache também guarda a transcrição completa da sessão — saída de ferramenta incluída, o que dá dezenas de megabytes numa sessão com cem chamadas de ferramenta. Num gateway movimentado, com várias plataformas, o cache é, portanto, o maior consumidor isolado de memória do processo.
agent:
agent_cache:
max_size: 128 # Teto de entradas do LRU
idle_ttl_secs: 3600 # Descarta um agente parado por este tempo
memory_high_mb: auto # Orçamento de RSS anônima; um número, "auto" ou 0/off
max_evictions_per_pass: 16
protect_recent: 8
max_size e idle_ttl_secs limitam o cache por quantidade e por tempo. Nenhum dos dois sabe quantos bytes o cache guarda, então memory_high_mb acrescenta um terceiro limite: assim que a memória residente anônima do próprio gateway passa do orçamento, ele descarta as transcrições menos usadas recentemente, que voltam a carregar da sessão armazenada no turno seguinte. Diminua se o gateway está disputando memória com outros serviços; aumente (ou coloque 0 para desligar a passada) se você prefere manter todo prefixo quente.
auto deriva o orçamento do limite de memória sob o qual o gateway realmente roda — o limite do cgroup, num contêiner ou unidade do systemd; a RAM total, caso contrário — para que um MemoryMax/MemoryHigh na unidade seja respeitado sem um segundo número para manter em sincronia.
Sessões no meio de um turno, as protect_recent usadas mais recentemente e qualquer sessão cuja transcrição ainda não terminou de ser escrita no disco nunca são descartadas. A remoção é registrada em WARNING, com a RSS medida e as sessões descartadas:
Agent cache pressure: anon RSS 6802MB over budget 6656MB — evicting 5 LRU session(s): ...
Motor de contexto
O motor de contexto controla como as conversas são gerenciadas quando se aproximam do limite de tokens do modelo. O motor compressor embutido usa resumo com perdas (veja Compressão de contexto). Motores de plugin podem substituí-lo por estratégias alternativas.
context:
engine: "compressor" # padrão — resumo com perdas embutido
Para usar um motor de plugin (por exemplo, LCM para gerenciamento de contexto sem perdas):
context:
engine: "lcm" # precisa bater com o nome do plugin
Motores de plugin nunca são ativados sozinhos — você tem que definir context.engine com o nome do plugin explicitamente. Dá para navegar pelos motores disponíveis e escolher um em hermes plugins → Provider Plugins → Context Engine.
Veja Provedores de memória para o sistema análogo de escolha única dos plugins de memória.
Orçamento de iterações
Quando o agente está numa tarefa complexa, com muitas chamadas de ferramenta, ele pode queimar o orçamento de iterações (padrão: 500 turnos). O Hermes não injeta avisos de pressão no meio da tarefa — versões anteriores avisavam o modelo em 70%/90% do orçamento, o que fazia os modelos abandonarem tarefas complexas cedo demais, e isso foi removido em abril de 2026.
Em vez disso, quando o orçamento realmente acaba (500/500), o Hermes injeta uma mensagem pedindo ao modelo que feche o assunto e permite uma única chamada de cortesia, para que ele consiga entregar uma resposta final. Se essa chamada de cortesia ainda assim não produzir texto, pede-se ao agente que resuma o que conseguiu fazer.
agent:
max_turns: none # Iterações por turno de conversa (padrão: none = ilimitado)
# Defina um inteiro positivo para limitar; "none"/"null"/
# "unlimited"/"inf"/"infinity"/"infinite"/0/-1 = sem limite
api_max_retries: 3 # Tentativas por provedor antes de a reserva entrar (padrão: 3)
agent.max_turns é ilimitado por padrão — o teto de turnos causava mais problema do que resolvia (truncamento silencioso no meio da tarefa), então, de fábrica, o Hermes leva um turno de conversa até o fim. Para impor um teto, defina um inteiro positivo. Para dizer "sem limite" de forma explícita, qualquer uma destas grafias funciona, sem diferenciar maiúsculas: "none", "null", "unlimited", "infinite", "infinity", "inf", 0, -1 (todas resolvem para uma sentinela sys.maxsize, então o laço nunca sai por contagem de turnos).
agent.api_max_retries controla quantas vezes o Hermes tenta de novo uma chamada de API de provedor em erros passageiros (limites de taxa, quedas de conexão, 5xx) antes de a troca para provedor de reserva entrar. O padrão é 3 — quatro tentativas no total. Se você tem provedores de reserva configurados e quer trocar mais rápido, baixe para 0, para que o primeiro erro passageiro no principal passe imediatamente para a reserva em vez de martelar tentativas contra o endpoint instável.
Orçamento de tempo de execução
Separado do orçamento de iterações, você pode dar a cada execução de conversa um orçamento opcional de tempo de relógio. Isso foi feito para invocações de tiro único e de arcabouço de avaliação que rodam sob um teto externo rígido (por exemplo, um limite de 900 segundos por tarefa): sem ele, uma execução pode estourar o tempo com o trabalho praticamente pronto — a uma geração de emitir a resposta final, ou presa numa única chamada de provedor travada.
agent:
run_budget_seconds: null # Opcional; sem definir/null = recurso totalmente desligado (padrão)
Ou por invocação, pela CLI:
hermes chat --run-budget 850 -q "..."
Quando há um orçamento definido, duas coisas acontecem:
- Aviso de fechamento aos 80%. Quando 80% do orçamento passou, o Hermes injeta um aviso único (entregue de forma segura para o cache, acrescentado ao resultado de ferramenta mais novo, como as mensagens de
/steer) dizendo ao modelo para parar de descobrir/verificar coisas novas e produzir o entregável final com o estado que já tem. Dispara no máximo uma vez por execução e espelha o mecanismo já existente de fechamento por orçamento de iterações — não há avisos repetidos de pressão. - Tempos limite de travamento proporcionais ao prazo. Os tempos limite implícitos de travamento sem streaming (o padrão de 90s e os pisos de modelo de raciocínio, por exemplo 600s para os modelos de raciocínio da DeepSeek) são limitados a
max(60, orçamento_restante × 0,5), para que uma única chamada de provedor travada em silêncio nunca consuma o resto da execução. O teto só aperta o tempo limite — nunca aumenta —, e umstale_timeout_secondsconfigurado explicitamente (na configuração de provedor/modelo ou emHERMES_API_CALL_STALE_TIMEOUT) sempre vence, intocado.
O orçamento é por turno de run_conversation (ele zera a cada mensagem do usuário), e o recurso fica completamente adormecido quando não é definido — nenhuma leitura de relógio, nenhuma injeção, nenhuma mudança de tempo limite.
Verificar ao parar (verificação de código)
Quando ligado, o Hermes se recusa a aceitar uma resposta final num turno em que o agente editou código num espaço de trabalho mas não produziu evidência nova de verificação (um teste passando, uma build, um lint etc.) — ele injeta uma continuação sintética pedindo ao agente que verifique ou explique por que não consegue. Edições só de documentação/markdown/skill nunca disparam isso, e o laço é limitado, então nunca consegue prender o agente.
agent:
verify_on_stop: false # true | false | "auto" (ciente da superfície: ligado para CLI/TUI/desktop, desligado para mensagens)
verify_guidance: true # Acrescenta orientação de interface criativa / diff limpo ao empurrãozinho de evidência faltando
max_verify_nudges: 3 # Teto de empurrõezinhos consecutivos de continuação por turno (embutidos + hooks pre_verify)
coding_instructions: "" # Regras de código permanentes de todo o projeto, acrescentadas ao briefing de programação
verify_on_stop aceita true (ligado em todo lugar), false (desligado — o padrão) ou "auto" (comportamento antigo, ciente da superfície: ligado nas superfícies interativas de programação — CLI, TUI, desktop — e para chamadores programáticos; desligado nas superfícies de mensagem, como Telegram/Discord, onde a narrativa de verificação parece barulho de conversa). Desligado é o padrão em todo lugar: instalações novas vêm com false, e a migração de configuração desligou nas instalações existentes, então ligar é uma escolha explícita. A variável de ambiente HERMES_VERIFY_ON_STOP sobrescreve o valor da configuração quando está definida.
Para uma trava de política de usuário/plugin no mesmo ponto — manter o agente andando com as suas próprias checagens —, veja o hook pre_verify.
Metas permanentes (/goal)
Quando uma meta permanente está ativa, o Hermes julga se cada resposta do assistente a satisfaz. Se não satisfizer, ele alimenta um prompt de continuação de volta na mesma sessão e continua trabalhando até a meta ficar pronta, o orçamento de turnos acabar ou o usuário pausar/limpar a meta. O orçamento de turnos é a rede de segurança de verdade — falhas do juiz falham abertas (continuam), então um juiz instável nunca emperra o progresso.
goals:
max_turns: 20 # Máximo de turnos de continuação antes de o Hermes pausar a meta sozinho (padrão: 20)
max_turns limita quantos turnos de continuação uma meta pode conduzir antes de o Hermes pausá-la sozinho e pedir ao usuário um /goal resume. Protege contra falsos negativos do juiz (a meta está pronta, mas o juiz manda continuar) e contra gasto ilimitado de modelo em metas vagas ou impossíveis. Veja Metas para o recurso completo.
Tempos limite de API
O Hermes tem camadas separadas de tempo limite para streaming, além de um detector de travamento para chamadas sem streaming. Os detectores de travamento se ajustam sozinhos para provedores locais apenas quando você os deixa nos padrões implícitos.
| Tempo limite | Padrão | Provedores locais | Configuração / variável de ambiente |
|---|---|---|---|
| Tempo limite de leitura de socket | 120s | Sobe sozinho para 1800s | HERMES_STREAM_READ_TIMEOUT |
| Detecção de fluxo travado | 180s | Sobe para um teto de 900s (agent.local_stream_stale_timeout) | HERMES_STREAM_STALE_TIMEOUT |
| Detecção de travamento sem streaming | 90s | Desligada sozinha quando fica implícita | providers.<id>.stale_timeout_seconds ou HERMES_API_CALL_STALE_TIMEOUT |
| Chamada de API (sem streaming) | 1800s | Inalterado | providers.<id>.request_timeout_seconds / timeout_seconds ou HERMES_API_TIMEOUT |
O tempo limite de leitura de socket controla quanto tempo o httpx espera pelo próximo pedaço de dados do provedor. LLMs locais podem levar minutos no preenchimento inicial de contextos grandes antes de produzir o primeiro token, então o Hermes sobe isso para 30 minutos quando detecta um endpoint local. Se você definir HERMES_STREAM_READ_TIMEOUT explicitamente, esse valor é sempre usado, independentemente da detecção de endpoint.
A detecção de fluxo travado mata conexões que recebem pings de keep-alive do SSE mas nenhum conteúdo de verdade. Para provedores locais (que não mandam pings de keep-alive durante o preenchimento inicial), o padrão sobe para um teto finito de 900 segundos em vez da base de 180s — configurável por agent.local_stream_stale_timeout ou pela variável de ambiente HERMES_LOCAL_STREAM_STALE_TIMEOUT.
A detecção de travamento sem streaming mata chamadas sem streaming que ficam tempo demais sem produzir resposta. Por padrão, o Hermes desliga isso em endpoints locais, para evitar falsos positivos durante preenchimentos iniciais longos. Se você definir explicitamente providers.<id>.stale_timeout_seconds, providers.<id>.models.<model>.stale_timeout_seconds ou HERMES_API_CALL_STALE_TIMEOUT, esse valor explícito é respeitado até em endpoints locais.
Esse orçamento limita toda chamada sem streaming, inclusive as que tarefas de cron e subagentes delegados rodam em linha. Um provedor que aceita uma requisição e depois fica mudo — conexão aberta, nenhum byte, nenhum erro — é abortado no tempo limite de travamento e tentado de novo, em vez de ficar pendurado até o tempo limite de leitura de socket, bem mais longo (ou, numa execução de cron sem supervisão, até algo externo matar o processo).
Avisos de pressão de contexto
Separada da pressão de orçamento de iterações, a pressão de contexto acompanha o quanto a conversa está perto do limiar de compactação — o ponto em que a compressão de contexto dispara para resumir as mensagens mais antigas. Isso ajuda você e o agente a entenderem quando a conversa está ficando longa.
| Progresso | Nível | O que acontece |
|---|---|---|
| ≥ 60% do limiar | Informação | A CLI mostra uma barra de progresso ciano; o gateway manda um aviso informativo |
| ≥ 85% do limiar | Alerta | A CLI mostra uma barra amarela em negrito; o gateway avisa que a compactação está próxima |
Na CLI, a pressão de contexto aparece como uma barra de progresso no fluxo de saída das ferramentas:
◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction
Nas plataformas de mensagem, é mandada uma notificação em texto puro:
◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).
Se a compressão automática estiver desligada, o aviso te conta que o contexto pode ser truncado, em vez disso.
A pressão de contexto é automática — não precisa configurar nada. Ela existe puramente como notificação para o usuário e não altera o fluxo de mensagens nem injeta nada no contexto do modelo.
Estratégias de pool de credenciais
Quando você tem várias chaves de API ou tokens OAuth do mesmo provedor, configure a estratégia de rotação:
credential_pool_strategies:
openrouter: round_robin # percorre as chaves de forma equilibrada
anthropic: least_used # sempre escolhe a chave menos usada
Opções: fill_first (padrão), round_robin, least_used, random. Veja Pools de credenciais para a documentação completa.
Cache de prompt
O Hermes liga o cache de prompt entre sessões automaticamente quando o provedor ativo suporta — não precisa configurar nada.
Para o Claude no Anthropic nativo, no OpenRouter e no Nous Portal, o Hermes acrescenta pontos de corte de cache_control com TTL de 1 hora (ttl: "1h") no prompt de sistema e nos blocos de skill. O primeiro envio dentro de uma hora nova paga a tarifa cheia de entrada; os envios seguintes, em qualquer sessão dentro da mesma hora, saem do cache pela tarifa reduzida de leitura em cache. Isso significa que o prompt de sistema, o conteúdo de skill carregado e a parte inicial de qualquer inclusão de contexto longo são reaproveitados entre sessões do hermes e entre subagentes derivados durante a primeira hora.
O upstream do Qwen Cloud (Alibaba DashScope) limita o TTL de cache a 5 minutos, então lá o Hermes usa o TTL de ponto de corte de 5 minutos. Outros caminhos de Claude por terceiros (AWS Bedrock, Azure Foundry) caem para os padrões de cache do próprio provedor. O Grok da xAI usa um mecanismo separado, de id de conversa fixado à sessão — veja Cache de prompt da xAI.
Não existe botão para desligar isso — o cache está sempre ligado e economiza dinheiro até em conversas de um turno só, porque o prompt de sistema sozinho já é uma fração significativa da contagem de tokens de entrada.
O único botão explícito é a faixa de TTL de cache que o Hermes pede nos pontos de corte no estilo Anthropic:
prompt_caching:
cache_ttl: "5m" # "5m" ou "1h" (faixas suportadas pela Anthropic); outros valores são ignorados
cache_ttl escolhe o TTL de ponto de corte que o Hermes acrescenta para o Claude pela API nativa da Anthropic, pelo OpenRouter e pelo Nous Portal. Só as duas faixas suportadas pela Anthropic ("5m", "1h") são respeitadas — qualquer outro valor é ignorado. Provedores com tetos próprios (por exemplo, o Qwen Cloud, que vai no máximo a 5 minutos) continuam limitados ao que o upstream permite.
Modelos auxiliares
O Hermes usa modelos "auxiliares" para tarefas secundárias, como análise de imagem, análise de captura de tela do navegador, geração de título de sessão e compressão de contexto. Por padrão (auxiliary.*.provider: "auto"), o Hermes manda toda tarefa auxiliar para o seu modelo principal de chat — o mesmo provedor/modelo que você escolheu em hermes model. Você não precisa configurar nada para começar, mas saiba que, em modelos de raciocínio caros (Opus, MiniMax M2.7 etc.), as tarefas auxiliares somam um custo relevante. Se você quer tarefas secundárias baratas e rápidas independentemente do seu modelo principal, defina auxiliary.<task>.provider e auxiliary.<task>.model explicitamente (por exemplo, Gemini Flash no OpenRouter para visão). (Extração de web não é tarefa auxiliar: web_extract e as capturas do navegador truncam conteúdo longo de forma determinística e guardam o texto completo para paginação com read_file — nenhum LLM envolvido.)
Versões anteriores mandavam quem usava agregadores (OpenRouter, Nous Portal) para um padrão barato do lado do provedor. Isso era surpreendente — quem pagava uma assinatura de agregador via um modelo diferente cuidando do tráfego auxiliar. Hoje o auto usa o modelo principal para todo mundo, e as sobrescritas por tarefa no config.yaml continuam vencendo (veja Referência completa da configuração auxiliar abaixo).
Configurar modelos auxiliares de forma interativa
Em vez de editar YAML na mão, rode hermes model e escolha "Configure auxiliary models" no menu. Você ganha um seletor interativo por tarefa:
$ hermes model
→ Configure auxiliary models
[ ] vision currently: auto / main model
[ ] title_generation currently: openrouter / google/gemini-3-flash-preview
[ ] tts_audio_tags currently: auto / main model
[ ] compression currently: auto / main model
[ ] approval currently: auto / main model
[ ] triage_specifier currently: auto / main model
[ ] kanban_decomposer currently: auto / main model
[ ] profile_describer currently: auto / main model
[ ] delegation currently: auto / inherit main agent
Escolha uma tarefa, escolha um provedor (fluxos de OAuth abrem o navegador; provedores por chave de API perguntam), escolha um modelo. A mudança é gravada em auxiliary.<task>.* no config.yaml. É o mesmo maquinário do seletor do modelo principal — nenhuma sintaxe nova para aprender.
A entrada Delegation é especial: ela roteia o modelo usado pelos subagentes de delegate_task e é gravada na seção de topo delegation.* (delegation.provider / delegation.model), não em auxiliary.*, porque subagentes são agentes filhos completos, não chamadas de LLM secundárias. O auto dela significa "herda o provedor, o modelo e as credenciais do agente pai".
Se você não quer que o Hermes gere títulos sozinho depois da primeira troca, defina
auxiliary.title_generation.enabled: false. Títulos manuais continuam funcionando por
/title e hermes sessions rename.
Endpoints que só aceitam streaming
Alguns endpoints compatíveis com OpenAI recusam de cara requisições de chat sem streaming (por exemplo, o Copilot da Tencent devolve HTTP 400 "Non-stream chat request is currently not supported"). O chat interativo já usa streaming, mas as tarefas auxiliares (geração de título, compressão, visão) usam chamadas sem streaming e falhariam em toda tentativa. O Hermes sempre trata copilot.tencent.com como só de streaming; para qualquer outro endpoint assim, liste um pedaço da URL em auxiliary.stream_only_base_urls:
auxiliary:
stream_only_base_urls:
- "meu-proxy-so-streaming.exemplo.com"
As chamadas auxiliares que casam são mandadas com stream=True, e os pedaços (inclusive os deltas de chamada de ferramenta) são juntados do lado do cliente — nenhuma mudança de comportamento em outros endpoints.
Tutorial em vídeo
O padrão universal de configuração
Toda vaga de modelo no Hermes — tarefas auxiliares, compressão, reserva — usa os mesmos três botões:
| Chave | O que faz | Padrão |
|---|---|---|
provider | Qual provedor usar para autenticação e roteamento | "auto" |
model | Qual modelo pedir | o padrão do provedor |
base_url | Endpoint personalizado compatível com OpenAI (sobrescreve o provedor) | não definido |
Os blocos de tarefa auxiliar aceitam, além disso, um botão reasoning_effort:
| Chave | O que faz | Padrão |
|---|---|---|
reasoning_effort | Nível de raciocínio nas chamadas de LLM daquela tarefa: none, minimal, low, medium, high, xhigh, max, ultra | não definido (padrão do provedor) |
Esta é a contraparte por tarefa do agent.reasoning_effort global: rode a compressão em low ou a visão em none para cortar latência e custo das tarefas secundárias quando o seu modelo principal é um modelo de raciocínio caro, sem mexer no comportamento do seu chat principal. Funciona em todo bloco de tarefa auxiliar (vision, compression, title_generation, curator, background_review, ...), nos três formatos de fio auxiliares (chat completions, Codex Responses, Anthropic Messages). Um extra_body.reasoning explícito na mesma tarefa vence o atalho.
O MoA é a única exceção: a profundidade de raciocínio da Mistura de Agentes é configurada por vaga no preset de MoA (moa.presets.<name>.reference_models[].reasoning_effort / aggregator.reasoning_effort), não nos blocos auxiliares moa_reference/moa_aggregator — veja Mistura de Agentes.
auxiliary:
compression:
reasoning_effort: "low" # resumo não precisa de raciocínio profundo
vision:
reasoning_effort: "none" # desliga o raciocínio na descrição de imagem
Quando base_url está definido, o Hermes ignora o provedor e chama aquele endpoint direto (usando api_key ou OPENAI_API_KEY para autenticar). Quando só provider está definido, o Hermes usa a autenticação e a URL base embutidas daquele provedor.
Provedores disponíveis para tarefas auxiliares: auto, main, mais qualquer provedor do registro de provedores — openrouter, nous, openai-codex, copilot, copilot-acp, anthropic, gemini, qwen-oauth, zai, kimi-coding, kimi-coding-cn, minimax, minimax-cn, minimax-oauth, deepseek, nvidia, xai, xai-oauth, ollama-cloud, alibaba, bedrock, huggingface, arcee, xiaomi, kilocode, opencode-zen, opencode-go, opencode-free, commandcode, commandcode-anthropic, ai-gateway, azure-foundry — ou qualquer provedor personalizado nomeado no seu dicionário providers: (por exemplo, provider: "beans").
minimax-oauth faz login por OAuth no navegador (não precisa de chave de API). Rode hermes model e escolha MiniMax (OAuth) para autenticar. As tarefas auxiliares usam MiniMax-M2.7-highspeed automaticamente. Veja o guia de OAuth da MiniMax.
xai-oauth faz login por OAuth no navegador para assinantes do SuperGrok e do X Premium+ (não precisa de chave de API). Rode hermes model e escolha xAI Grok OAuth (SuperGrok / Premium+) para autenticar. O mesmo token OAuth é reaproveitado em toda superfície que fala direto com a xAI (chat, tarefas auxiliares, TTS, geração de imagem, geração de vídeo, transcrição). Veja o guia de OAuth do Grok da xAI e, se o Hermes estiver numa máquina remota, veja OAuth por SSH / máquinas remotas.
"main" é só para tarefas auxiliaresA opção de provedor "main" significa "usa o provedor que o meu agente principal usa" — só é válida dentro de auxiliary:, compression: e das entradas de reserva primárias (fallback_providers: ou o antigo fallback_model:). Não é um valor válido para a sua configuração de topo model.provider. Se você usa um endpoint personalizado compatível com OpenAI, defina provider: custom na sua seção model:. Veja Provedores de IA para todas as opções de provedor do modelo principal.
Referência completa da configuração auxiliar
auxiliary:
# Análise de imagem (ferramenta vision_analyze + capturas de tela do navegador)
vision:
provider: "auto" # "auto", "openrouter", "nous", "codex", "main" etc.
model: "" # por exemplo "openai/gpt-4o", "google/gemini-2.5-flash"
base_url: "" # Endpoint personalizado compatível com OpenAI (sobrescreve o provedor)
api_key: "" # Chave de API do base_url (cai para OPENAI_API_KEY)
timeout: 120 # segundos — tempo limite da chamada de API do LLM; cargas de visão precisam de folga
download_timeout: 30 # segundos — download HTTP da imagem; aumente em conexões lentas
max_concurrency: 8 # máximo de rajadas simultâneas de codificação/redimensionamento de imagem no processo
# (padrão: número de núcleos de CPU do host, sem teto) — limita apenas o passo
# de codificação preso à CPU, para que uma explosão de quadros de vídeo não sature
# todos os núcleos e mate o laço de eventos; as chamadas de LLM continuam
# totalmente concorrentes. Mínimo 1; valores < 1 são ignorados.
# Classificador de aprovação de comandos perigosos
approval:
provider: "auto"
model: ""
base_url: ""
api_key: ""
timeout: 30 # segundos
# Inserção de marcas de áudio ocultas no TTS do Gemini 3.1
tts_audio_tags:
provider: "auto"
model: "" # vazio = modelo principal de chat
base_url: ""
api_key: ""
timeout: 30
# Tempo limite da compressão de contexto (separado da configuração compression.*)
compression:
timeout: 120 # segundos — a compressão resume conversas longas, precisa de mais tempo
# fallback_chain: # Opcional — provedores a tentar em falha de limite de taxa / conectividade
# - provider: nous
# model: deepseek/deepseek-chat
# - provider: openrouter
# model: google/gemini-2.5-flash
# base_url: ""
# api_key: ""
# max_concurrency: 2 # Opcional: limita as chamadas simultâneas de LLM de compressão para
# várias sessões não empilharem tentativas num provedor degradado
# Títulos de sessão gerados automaticamente. Idioma vazio acompanha a conversa;
# coloque, por exemplo, "English" ou "Japanese" para fixar os títulos num idioma.
title_generation:
enabled: true # coloque false para desligar a geração automática de título
provider: "auto"
model: ""
base_url: ""
api_key: ""
timeout: 30
language: ""
# Central de skills — casamento e busca de skills
skills_hub:
provider: "auto"
model: ""
base_url: ""
api_key: ""
timeout: 30
# Despacho de ferramentas MCP
mcp:
provider: "auto"
model: ""
base_url: ""
api_key: ""
timeout: 30
# Títulos curtos de sessão gerados automaticamente depois da primeira troca
title_generation:
provider: "auto"
model: ""
base_url: ""
api_key: ""
timeout: 30
# max_concurrency: 2 # Opcional: limita as chamadas simultâneas de geração de título
# Especificador de triagem do kanban — `hermes kanban specify <id>` (ou o
# botão ✨ Specify do painel, nos cartões da coluna Triage) usa esta
# vaga para expandir uma frase solta numa especificação concreta e promover
# a tarefa para `todo`. Modelos baratos e rápidos funcionam bem aqui; a
# expansão de especificação é curta e não precisa de profundidade de raciocínio.
triage_specifier:
provider: "auto"
model: ""
base_url: ""
api_key: ""
timeout: 120
Cada tarefa auxiliar tem um timeout configurável (em segundos). Padrões: visão 120s, aprovação 30s, compressão 120s. Aumente se você usa modelos locais lentos nas tarefas auxiliares. A visão também tem um download_timeout separado (padrão 30s) para o download HTTP da imagem — aumente em conexões lentas ou servidores de imagem auto-hospedados.
A compressão de contexto tem o próprio bloco compression: para os limiares e um bloco auxiliary.compression: para as configurações de modelo/provedor — veja Compressão de contexto acima. A cadeia de reserva primária usa uma lista fallback_providers: de nível superior — veja Provedores de reserva. Os três seguem o mesmo padrão de provider/model/base_url.
Cadeia de reserva por tarefa auxiliar
Cada tarefa auxiliar pode definir, opcionalmente, uma fallback_chain — uma lista de entradas de provedor/modelo que o Hermes tenta quando o provedor auxiliar principal falha por limite de taxa, problema de conectividade ou restrição de pagamento:
auxiliary:
compression:
provider: openrouter
model: openai/gpt-4o-mini
fallback_chain:
- provider: nous
model: deepseek/deepseek-chat
- provider: openrouter
model: google/gemini-2.5-flash
Quando o provedor auxiliar principal (openrouter / openai/gpt-4o-mini) devolve um erro de limite de taxa, de tempo esgotado de conexão ou de pagamento exigido, o Hermes percorre a fallback_chain na ordem. Ele pula as entradas cujo provedor é o mesmo que já falhou e tenta cada entrada restante até uma dar certo ou a cadeia acabar. Se todas as reservas falharem, o Hermes cai para o modelo principal do agente, como última rede de segurança.
Cada entrada suporta os mesmos três botões de qualquer configuração de tarefa auxiliar:
| Chave | Descrição |
|---|---|
provider | Nome do provedor (nous, openrouter, anthropic, gemini, main etc.) |
model | Nome do modelo naquele provedor |
base_url | (Opcional) Endpoint personalizado compatível com OpenAI |
fallback_chain está disponível em qualquer tarefa auxiliar — compression, vision, approval, skills_hub, mcp etc.
Limitar a concorrência auxiliar
max_concurrency limita as chamadas de LLM em andamento das tarefas auxiliares, como compression e title_generation, no processo inteiro. auxiliary.vision.max_concurrency fica de fora: ele já controla apenas os trabalhadores de codificação/redimensionamento de imagem da visão, presos à CPU, não as requisições de LLM. Isso é mais útil quando:
- Muitas sessões podem disparar trabalho em segundo plano ao mesmo tempo (canais de Discord/Telegram, vários terminais)
- O seu provedor está com limite de taxa ou passando por um incidente, e as tentativas amplificariam a rajada
O padrão é ilimitado. Um teto de segurança típico é 2:
auxiliary:
title_generation:
max_concurrency: 2
compression:
max_concurrency: 2
O semáforo envolve a chamada inteira, incluindo tentativas e reservas, então uma única chamada lenta conta uma vez só para o limite.
Roteamento do OpenRouter e Pareto Code nas tarefas auxiliares
Quando uma tarefa auxiliar cai no OpenRouter (explicitamente ou por provider: "main" com o seu agente principal no OpenRouter), as configurações provider_routing e openrouter.min_coding_score do agente principal não se propagam — de propósito, cada tarefa auxiliar é independente. Para definir preferências de provedor do OpenRouter ou usar o roteador Pareto Code numa tarefa auxiliar específica, defina por tarefa, via extra_body:
auxiliary:
compression:
provider: openrouter
model: openrouter/pareto-code # usa o roteador Pareto Code nesta tarefa
extra_body:
provider: # preferências de roteamento de provedor do OpenRouter
order: [anthropic, google] # tenta estes provedores, nesta ordem
sort: throughput # ou "price" | "latency"
# only: [anthropic] # restringe a um provedor específico
# ignore: [deepinfra] # exclui provedores específicos
plugins: # botão do roteador Pareto Code do OpenRouter
- id: pareto-router
min_coding_score: 0.5 # 0.0–1.0; mais alto = programadores mais fortes
O formato espelha o que o OpenRouter aceita no corpo da requisição de chat completions. O Hermes repassa o extra_body inteiro, literalmente, então qualquer outro campo de corpo de requisição do OpenRouter documentado em openrouter.ai/docs funciona do mesmo jeito.
Trocar o modelo de visão
Para usar o GPT-4o em vez do Gemini Flash na análise de imagem:
auxiliary:
vision:
model: "openai/gpt-4o"
Ou por variável de ambiente (no ~/.hermes/.env):
AUXILIARY_VISION_MODEL=openai/gpt-4o
Opções de provedor
Estas opções valem para as configurações de tarefa auxiliar (auxiliary:, compression:) e para as entradas de reserva primárias (fallback_providers: ou o antigo fallback_model:), não para a sua configuração principal model.provider.
| Provedor | Descrição | Requisitos |
|---|---|---|
"auto" | O melhor disponível (padrão). A visão tenta OpenRouter → Nous → Codex. | — |
"openrouter" | Força o OpenRouter — roteia para qualquer modelo (Gemini, GPT-4o, Claude etc.) | OPENROUTER_API_KEY |
"nous" | Força o Nous Portal | hermes auth |
"codex" | Força o OAuth do Codex (conta do ChatGPT). Suporta visão (gpt-5.3-codex). | hermes model → ChatGPT ou assinatura do Codex |
"minimax-oauth" | Força o OAuth da MiniMax (login no navegador, sem chave de API). Usa MiniMax-M2.7-highspeed nas tarefas auxiliares. | hermes model → MiniMax (OAuth) |
"xai-oauth" | Força o OAuth do Grok da xAI (login no navegador para assinantes do SuperGrok ou do X Premium+, sem chave de API). O mesmo token OAuth cobre chat, TTS, imagem, vídeo e transcrição. | hermes model → xAI Grok OAuth (SuperGrok / Premium+) |
"main" | Usa o seu endpoint principal/personalizado ativo. Pode vir de OPENAI_BASE_URL + OPENAI_API_KEY ou de um endpoint personalizado salvo por hermes model / config.yaml. Funciona com OpenAI, modelos locais ou qualquer API compatível com OpenAI. Só para tarefas auxiliares — não vale para model.provider. | Credenciais do endpoint personalizado + URL base |
Provedores diretos, por chave de API, do catálogo principal também funcionam aqui, quando você quer que as tarefas secundárias passem por fora do seu roteador padrão. Por exemplo, gmi é válido depois que GMI_API_KEY está configurada, e fireworks é válido depois que FIREWORKS_API_KEY está configurada:
auxiliary:
compression:
provider: "gmi"
model: "anthropic/claude-opus-4.6"
No roteamento auxiliar pela GMI, use o ID de modelo exato devolvido pelo endpoint /v1/models da GMI. Os IDs de modelo da Fireworks usam a forma nativa com barras do provedor, por exemplo accounts/fireworks/models/glm-5p2.
Configurações comuns
Usar um endpoint personalizado direto (mais claro que provider: "main" para APIs locais/auto-hospedadas):
auxiliary:
vision:
base_url: "http://localhost:1234/v1"
api_key: "local-key"
model: "qwen2.5-vl"
base_url tem precedência sobre provider, então esta é a forma mais explícita de rotear uma tarefa auxiliar para um endpoint específico. Nas sobrescritas diretas de endpoint, o Hermes usa a api_key configurada ou cai para OPENAI_API_KEY; ele não reaproveita OPENROUTER_API_KEY naquele endpoint personalizado.
Usar a chave de API da OpenAI para visão:
# No ~/.hermes/.env:
# OPENAI_BASE_URL=https://api.openai.com/v1
# OPENAI_API_KEY=sk-...
auxiliary:
vision:
provider: "main"
model: "gpt-4o" # ou "gpt-4o-mini", mais barato
Usar o OpenRouter para visão (roteia para qualquer modelo):
auxiliary:
vision:
provider: "openrouter"
model: "openai/gpt-4o" # ou "google/gemini-2.5-flash" etc.
Usar o OAuth do Codex (conta ChatGPT Pro/Plus — não precisa de chave de API):
auxiliary:
vision:
provider: "codex" # usa o seu token OAuth do ChatGPT
# o modelo padrão é gpt-5.3-codex (suporta visão)
Usar o OAuth da MiniMax (login no navegador, sem chave de API):
model:
default: MiniMax-M2.7
provider: minimax-oauth
base_url: https://api.minimax.io/anthropic
Rode hermes model e escolha MiniMax (OAuth) para entrar e configurar isso automaticamente. Na região da China, a URL base é https://api.minimaxi.com/anthropic. Veja o guia de OAuth da MiniMax para o passo a passo completo.
Usar um modelo local/auto-hospedado:
auxiliary:
vision:
provider: "main" # usa o seu endpoint personalizado ativo
model: "meu-modelo-local"
provider: "main" usa o provedor que o Hermes usa no chat normal — seja um provedor personalizado nomeado (por exemplo, beans), um provedor embutido como o openrouter ou um endpoint antigo por OPENAI_BASE_URL.
Se você usa o OAuth do Codex como provedor do seu modelo principal, a visão funciona automaticamente — nada mais para configurar. O Codex está na cadeia de detecção automática de visão.
Visão exige um modelo multimodal. Se você definir provider: "main", confira que o seu endpoint suporta multimodal/visão — senão a análise de imagem vai falhar.
Variáveis de ambiente (forma antiga)
Os modelos auxiliares também podem ser configurados por variáveis de ambiente. Ainda assim, o config.yaml é o método preferido — é mais fácil de gerenciar e suporta todas as opções, inclusive base_url e api_key.
| Configuração | Variável de ambiente |
|---|---|
| Provedor de visão | AUXILIARY_VISION_PROVIDER |
| Modelo de visão | AUXILIARY_VISION_MODEL |
| Endpoint de visão | AUXILIARY_VISION_BASE_URL |
| Chave de API de visão | AUXILIARY_VISION_API_KEY |
As configurações de compressão e de modelo de reserva existem só no config.yaml. (As variáveis AUXILIARY_WEB_EXTRACT_* estão obsoletas — a extração de web não usa mais um LLM auxiliar.)
Rode hermes config para ver as suas configurações atuais de modelo auxiliar. As sobrescritas só aparecem quando diferem dos padrões.
Esforço de raciocínio
Controle quanto o modelo "pensa" antes de responder:
agent:
reasoning_effort: "" # vazio = medium. Opções: none, minimal, low, medium, high, xhigh, max, ultra
Sem definir (o padrão), o esforço de raciocínio fica em "medium" — um nível equilibrado, que funciona bem na maioria das tarefas. Definir um valor sobrescreve isso — mais esforço de raciocínio dá resultados melhores em tarefas complexas, ao custo de mais tokens e mais latência.
Esses modelos usam raciocínio adaptativo e não aceitam o campo reasoning.effort
de sempre — o OpenRouter o ignora para eles. O Hermes roteia o seu
reasoning_effort de forma transparente para o parâmetro verbosity do OpenRouter (que corresponde ao
output_config.effort da Anthropic), então o mesmo botão de esforço continua funcionando com
os níveis suportados pelo modelo escolhido. none (ou não definir) deixa o modelo
no próprio padrão adaptativo. O
provedor Anthropic nativo já controla o esforço direto e não é afetado.
Para os outros modelos roteados pelo OpenRouter, o Hermes lê os metadados de raciocínio do
catálogo vivo de modelos (supported_parameters + o reasoning.supported_efforts de cada modelo)
para decidir se manda controles de raciocínio ou não e para limitar o esforço pedido ao nível mais
próximo que a rota realmente suporta (sempre para baixo — por exemplo, ultra vira high numa rota que para
em high, nunca uma escalada silenciosa). Fornecedores novos com capacidade de raciocínio funcionam
automaticamente, sem esperar uma atualização do Hermes; quando o catálogo está inacessível
ou um modelo não está listado, o Hermes cai para a lista embutida de famílias de
modelo e repassa o seu esforço sem mudanças.
Você também pode mudar o esforço de raciocínio em tempo de execução, com o comando /reasoning:
/reasoning # Mostra o nível de esforço atual e o estado de exibição
/reasoning high # Define o esforço de raciocínio como high (só nesta sessão)
/reasoning high --global # Define o esforço e grava no config.yaml
/reasoning none # Desliga o raciocínio (só nesta sessão)
/reasoning show # Mostra o pensamento do modelo acima de cada resposta
/reasoning hide # Esconde o pensamento do modelo
As mudanças de esforço valem só na sessão, por padrão; acrescente --global para salvar o
novo nível como o seu padrão de agent.reasoning_effort.
Sobrescritas de raciocínio por modelo
Você pode definir níveis diferentes de esforço de raciocínio para modelos diferentes. Isso é útil quando você quer raciocínio alto em modelos complexos, mas médio nos mais rápidos:
agent:
reasoning_effort: "medium" # padrão global
reasoning_overrides:
"openrouter/anthropic/claude-opus-4.5": "xhigh"
"openai/gpt-5": "low"
"claude-sonnet-4.6": "high" # o nome puro do modelo também funciona
O casamento de chave tolera variações de grafia — qualquer grafia razoável casa:
claude-opus-4.5,claude-opus-4-5,claude-opus.4.5(pontos e hifens são intercambiáveis)anthropic/claude-opus-4.5,openrouter/anthropic/claude-opus-4.5(o prefixo de provedor é opcional)- Correspondências exatas têm precedência sobre variantes
Não existe suporte do hermes config set para as chaves de reasoning_overrides — edite o arquivo YAML direto. Isso acontece porque nomes de modelo costumam ter pontos (por exemplo, claude-opus-4.5), que conflitam com a sintaxe de chaves pontuadas da CLI.
Prioridade de resolução:
- Sobrescrita
/reasoning --session, no escopo da sessão (só no gateway) - Sobrescrita por modelo, de
agent.reasoning_overrides(tolerante à grafia) agent.reasoning_effortglobal- Padrão do provedor
A sobrescrita se aplica automaticamente em todo lugar: inicialização da CLI, gateway de mensagens, Desktop/TUI, tarefas de cron, trocas de /model no meio da sessão e ativação de modelo de reserva.
Cobrança do uso de ferramentas
Alguns modelos, de vez em quando, descrevem em texto as ações que pretendem fazer em vez de chamar as ferramentas ("Eu rodaria os testes..." em vez de realmente chamar o terminal). A cobrança do uso de ferramentas injeta uma orientação no prompt de sistema que traz o modelo de volta a chamar as ferramentas de verdade.
agent:
tool_use_enforcement: "auto" # "auto" | true | false | ["pedaço-do-nome-do-modelo", ...]
| Valor | Comportamento |
|---|---|
"auto" (padrão) | Ligado para os modelos que casam com: gpt, codex, gemini, gemma, grok, glm, qwen, deepseek. Desligado para todos os outros (por exemplo, Claude). |
true | Sempre ligado, seja qual for o modelo. Útil se você percebe o seu modelo atual descrevendo ações em vez de executá-las. |
false | Sempre desligado, seja qual for o modelo. |
["gpt", "codex", "qwen", "llama"] | Ligado só quando o nome do modelo contém um dos pedaços listados (sem diferenciar maiúsculas). |
O que ele injeta
Quando ligado, duas camadas de orientação podem ser acrescentadas ao prompt de sistema:
-
Cobrança geral de uso de ferramentas (todos os modelos que casam) — instrui o modelo a fazer as chamadas de ferramenta na hora, em vez de descrever intenções, a continuar trabalhando até a tarefa terminar e a nunca fechar um turno com promessa de ação futura.
-
Orientação operacional do Google (só nos modelos Gemini e Gemma) — concisão, caminhos absolutos, chamadas de ferramenta em paralelo e o padrão de verificar antes de editar.
Isso é transparente para o usuário e só afeta o prompt de sistema. Modelos que já usam ferramentas de forma confiável (como o Claude) não precisam dessa orientação, e é por isso que o "auto" os deixa de fora.
Quando ligar
Se você usa um modelo que não está na lista automática padrão e percebe que ele descreve com frequência o que faria em vez de fazer, defina tool_use_enforcement: true ou acrescente o pedaço do nome do modelo à lista:
agent:
tool_use_enforcement: ["gpt", "codex", "gemini", "grok", "meu-modelo-personalizado"]
Orientação de disciplina de execução
Separadamente da cobrança de uso de ferramentas, o Hermes injeta um bloco de disciplina de execução para famílias de modelo que compartilham um conjunto de modos de falha agênticos observados em traços de avaliação: fazer conta em prosa em vez de em código, pular a verificação por releitura depois de escritas externas, "consertar" identificadores malformados, alegar completude apesar de contagens que não batem e declarar "pronto" sem verificar todo critério de aceitação.
agent:
execution_guidance: "auto" # "auto" | true | false | ["pedaço-do-nome-do-modelo", ...]
| Valor | Comportamento |
|---|---|
"auto" (padrão) | Ligado para os modelos que casam com: gpt, codex, grok, deepseek, kimi, qwen, glm, minimax, mimo, mistral. |
true | Sempre ligado, seja qual for o modelo. |
false | Sempre desligado, seja qual for o modelo. |
["deepseek", "meu-modelo-personalizado"] | Ligado só quando o nome do modelo contém um dos pedaços listados (sem diferenciar maiúsculas). |
O bloco injetado cobre:
- Persistência com ferramentas — continue chamando ferramentas até a tarefa estar completa e verificada; tente de novo, com uma consulta mais ampla ou diferente, resultados de busca vazios, parciais ou suspeitamente estreitos, antes de concluir qualquer coisa.
- Uso obrigatório de ferramenta — contas, hashes, datas, estado do sistema e fatos sobre arquivos sempre vêm de uma ferramenta, nunca de cálculo mental.
- Releitura depois de escrita externa — depois de qualquer escrita que muda o estado de um sistema externo, releia o alvo exato antes de alegar sucesso (edições internas de arquivo que uma ferramenta já confirmou não são reverificadas).
- Conciliação de contagem — totais declarados (
total,reply_count,has_more) são afirmações duras; quando não batem, busque de novo ou processe programaticamente. - Preservação literal — nunca normalize nem "conserte" identificadores que não passam num formato declarado; uma busca bem-sucedida não valida um token de origem malformado.
- Conclusão condicionada à verificação — "pronto" significa que todo critério de aceitação nomeado foi verificado, nunca um subconjunto plausível.
A trava é independente de tool_use_enforcement — uma pode estar ligada sem a outra. A orientação é escolhida uma vez, no início da sessão, com base no nome do modelo, então o prompt de sistema fica estável byte a byte (e amigável ao cache de prompt) pela vida inteira da conversa. Gemini/Gemma ficam fora da lista automática porque recebem a orientação operacional do Google, mais específica; o Claude fica de fora porque não apresenta esses modos de falha — inscreva qualquer modelo com true ou com uma lista de pedaços de nome.
Guardas contra laços de ferramenta
O Hermes detecta quando o agente está preso num laço improdutivo de chamadas de ferramenta — a mesma chamada falhando várias vezes, a mesma ferramenta falhando repetidamente ou uma chamada idempotente devolvendo o mesmo resultado sem progresso. Por padrão, ele injeta um aviso no resultado da ferramenta para o modelo se corrigir; não interrompe de vez, já que quem está olhando a CLI/TUI pode intervir.
Em implantações sem supervisão, de gateway/servidor, ligue as paradas duras, para que um agente preso seja interrompido em vez de queimar o orçamento de iterações:
tool_loop_guardrails:
warnings_enabled: true # injeta avisos nos resultados de ferramenta (padrão: true)
hard_stop_enabled: false # além disso, BLOQUEIA a chamada depois do limiar de parada dura (padrão: false)
warn_after:
exact_failure: 2 # chamada idêntica com falha repetida N vezes
same_tool_failure: 3 # mesma ferramenta falhando N vezes (com argumentos diferentes)
idempotent_no_progress: 2 # mesmo resultado, sem progresso, N vezes
hard_stop_after:
exact_failure: 5
same_tool_failure: 8
idempotent_no_progress: 5
loop_caps:
max_web_searches: 50 # máximo de chamadas de web_search por turno (0 = ilimitado)
max_subagents: 50 # máximo de subagentes criados por turno (0 = ilimitado)
hard_stop_enabled é false por padrão porque sessões interativas têm uma pessoa acompanhando. Em implantações sem supervisão (gateway, cron, trabalhadores de kanban), coloque true, para que falhas repetidas sejam bloqueadas em vez de só avisadas. Veja também Docker / implantações sem supervisão.
Tetos de laço descontrolado por turno
Separado dos limiares por falha acima, loop_caps define tetos duros para quantas chamadas de web_search e quantas criações de subagente um único laço de agente (turno) pode fazer. Os contadores zeram no começo de cada turno, então uma sessão legítima com vários turnos nunca passa fome — mas um único turno que degringola num laço ilimitado de busca ou de delegação é interrompido. Esses tetos estão sempre ligados e disparam independentemente de hard_stop_enabled. Um único turno disparando dezenas de buscas na web ou criando dezenas de subagentes já é patológico, então os padrões são baixos. Quando um teto é atingido, a chamada de ferramenta ofensora é bloqueada com uma mensagem explicativa e o turno para de forma limpa, em vez de queimar o resto do orçamento. Coloque 0 em qualquer um dos valores para desligar aquele teto por completo.
Um único lote de delegate_task conta cada tarefa no max_subagents (um lote de 3 gasta 3), então o teto acompanha os subagentes realmente criados, não as invocações de delegate_task.
Isso espelha os tetos de WebSearch e de subagente por sessão do Claude Code (v2.1.212), que também têm padrão 200 e zeram no /clear.
Guardas de tempo de execução contra travamento
Complementando as guardas por falha acima, agent.stall_guards (padrão true) liga duas guardas conservadoras, em tempo de execução, contra turnos desperdiçados. A primeira é um quebra-laço de chamadas idênticas: quando a mesma ferramenta é chamada 3 ou mais vezes seguidas com argumentos idênticos e devolve um resultado idêntico, um aviso curto de uma linha é acrescentado àquele resultado dizendo ao modelo para não repetir a chamada — nunca bloqueia a chamada, e verificadores que legitimamente se repetem (process, *_get_result, *_poll) ficam isentos. A segunda é uma recuperação de intenção de continuar: quando o modelo fecha um turno sem chamadas de ferramenta, mas a resposta curta dele termina anunciando uma ação ("Vou agora atualizar o arquivo…"), o Hermes o provoca de novo a agir, pelo mesmo mecanismo limitado de continuação usado na recuperação de reconhecimento de intenção (no máximo 2 provocações por turno). As duas são seguras para o cache (os avisos são acrescentados na construção do resultado, nunca de forma retroativa) e podem ser desligadas juntas:
agent:
stall_guards: false
A mesma trava também liga o uso de marcadores de referência de resultado: quando uma chamada de ferramenta idêntica, reemitida, devolve um resultado novo idêntico byte a byte, a carga duplicada entra no contexto como um marcador curto de referência apontando para o resultado anterior (nome da ferramenta, tool_call_id, um resumo dos argumentos e — se o primeiro resultado foi gravado no disco — o caminho de transbordo dele) em vez de repetir a saída completa. A ferramenta continua executando toda vez, então a semântica de verificação periódica é preservada: um resultado que mudou sempre passa inteiro. Resultados abaixo de 512 caracteres, resultados de erro e resultados multimodais nunca viram marcador, e verificadores periódicos viram (uma verificação sem mudança é exatamente o caso em que a carga duplicada não traz informação nenhuma).
Configuração de TTS
tts:
provider: "edge" # "edge" | "elevenlabs" | "openai" | "minimax" | "mistral" | "gemini" | "xai" | "neutts" | "kittentts" | "piper" | "deepinfra"
speed: 1.0 # Multiplicador global de velocidade (reserva para todos os provedores)
edge:
voice: "en-US-AriaNeural" # 322 vozes, 74 idiomas
speed: 1.0 # Multiplicador de velocidade (convertido em porcentagem de taxa, por exemplo 1.5 → +50%)
elevenlabs:
voice_id: "pNInz6obpgDQGcFmaJgB"
model_id: "eleven_multilingual_v2"
openai:
model: "gpt-4o-mini-tts"
voice: "alloy" # alloy, echo, fable, onyx, nova, shimmer
speed: 1.0 # Multiplicador de velocidade (limitado a 0.25–4.0 pela API)
base_url: "https://api.openai.com/v1" # Sobrescrita para endpoints de TTS compatíveis com OpenAI
minimax:
speed: 1.0 # Multiplicador de velocidade da fala
# base_url: "" # Opcional: sobrescrita para endpoints de TTS compatíveis com OpenAI
mistral:
model: "voxtral-mini-tts-2603"
voice_id: "c69964a6-ab8b-4f8a-9465-ec0925096ec8" # Paul - Neutral (padrão)
gemini:
model: "gemini-2.5-flash-preview-tts" # ou gemini-3.1-flash-tts-preview
voice: "Kore" # 30 vozes prontas: Zephyr, Puck, Kore, Enceladus etc.
audio_tags: false # Inserção de marcas de áudio ocultas do TTS do Gemini 3.1
persona_prompt_file: "" # Arquivo Markdown/texto opcional com a direção de voz do Gemini
xai:
voice_id: "eve" # Voz do TTS da xAI
language: "en" # ISO 639-1
sample_rate: 24000
bit_rate: 128000 # Taxa de bits do MP3
# base_url: "https://api.x.ai/v1"
neutts:
ref_audio: ''
ref_text: ''
model: neuphonic/neutts-air-q4-gguf
device: cpu
Isso controla tanto a ferramenta text_to_speech quanto as respostas faladas no modo de voz (/voice tts na CLI ou no gateway de mensagens).
Hierarquia de reserva da velocidade: velocidade específica do provedor (por exemplo, tts.edge.speed) → tts.speed global → padrão 1.0. Defina o tts.speed global para aplicar uma velocidade uniforme em todos os provedores, ou sobrescreva por provedor para um controle fino.
Configurações de exibição
display:
tool_progress: all # off | new | all | verbose
tool_progress_command: false # Liga o comando de barra /verbose no gateway de mensagens
focus_view: false # Visão de foco da CLI (/focus) — saída reduzida, só exibição
platforms: {} # Sobrescritas de exibição por plataforma (veja abaixo)
interim_assistant_messages: true # Gateway: manda as atualizações naturais do assistente no meio do turno como mensagens separadas
show_commentary: true # Modelos Codex: entrega a narração de progresso do canal de comentário como atualizações visíveis no meio do turno
skin: default # Skin embutida ou personalizada da CLI (veja user-guide/features/skins)
personality: "" # Campo cosmético antigo, ainda mostrado em alguns resumos
compact: false # Modo de saída compacta (menos espaço em branco)
cli_multiline_shortcuts: true # CLI: Ctrl+J, \ + Enter e o Shift+Enter suportado inserem quebras de linha (false = volta ao envio antigo com c-j)
resume_display: full # full (mostra as mensagens anteriores ao retomar) | minimal (só uma linha)
bell_on_complete: false # Toca o sino do terminal quando o agente termina (ótimo para tarefas longas)
show_reasoning: true # Mostra o raciocínio/pensamento do modelo acima de cada resposta (padrão: true; alterne com /reasoning show|hide)
streaming: false # Manda os tokens para o terminal conforme chegam (saída em tempo real)
show_cost: false # Mostra o custo estimado em $ na barra de status da CLI
timestamps: false # Quando true, coloca marcas de tempo antes dos rótulos de usuário e assistente na transcrição da CLI / TUI
timestamp_format: "%H:%M" # Formato strftime dessas marcas de tempo (por exemplo "%b-%d %H:%M" para mês-dia)
tool_preview_length: 0 # Máximo de caracteres nas prévias de chamada de ferramenta (0 = sem limite, mostra caminhos/comandos completos)
turn_summary: true # Só na CLI: imprime um rodapé de contabilidade de uma linha depois de cada turno interativo
spinner_token_flow: true # Só na CLI: acrescenta os tokens acumulados do turno, ao vivo, ao cronômetro do indicador de atividade
runtime_footer: # Gateway: acrescenta um rodapé de contexto de execução às respostas finais
enabled: false
fields: ["model", "context_pct", "cwd"]
file_mutation_verifier: true # Acrescenta um rodapé de aviso quando chamadas de write_file/patch falharam neste turno
credits_notices: true # Avisos de status dos créditos Nous na barra de status (faixas de uso, crédito gasto, esgotado). false = silencia; o /usage continua funcionando
cli_rebuild_scrollback_on_redraw: false # CLI clássica: também limpa o histórico de rolagem do terminal (CSI 3J) na recuperação por /redraw / Ctrl+L / mudança de largura. Ligue quando a sua pilha de terminal/tmux carimba restos velhos de prompt no histórico ao maximizar/restaurar.
language: en # Idioma da interface nas mensagens estáticas (pedidos de aprovação, algumas respostas do gateway). en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu
Resumo por turno e fluxo de tokens no indicador de atividade
display.turn_summary (padrão true) imprime uma linha apagada de contabilidade depois de cada turno interativo da CLI, resumindo o que aquele turno realmente fez:
⋯ 12.4s · edited 2 files +18 -3 · read 4 files · ran 3 commands
A contagem é observada no fluxo de progresso de ferramentas que a CLI já recebe, então não custa nada a mais. Detalhes:
- O tempo de relógio é a duração real do turno (
2m05sdepois de passar de um minuto). - As chamadas de ferramenta são agrupadas por verbo (
edited,read,ran,searched, …), com plural correto; ferramentas de plugin/MCP sem verbo curado viramcalled N tools. - Os deltas de linha
+X -Ysó aparecem quando o resultado da ferramenta já informa um diff (hoje, opatch). O Hermes nunca chama o git para calculá-los, então uma edição porwrite_fileé contada sem delta. - Chamadas de ferramenta que falharam não são contadas — uma escrita negada nunca aparece como edição bem-sucedida (veja o verificador de mutação de arquivo para o aviso complementar).
- Turnos longos param em quatro segmentos de verbo mais um
+N moreno fim, para a linha nunca quebrar. - Um turno rápido sem chamadas de ferramenta não imprime nada.
display.spinner_token_flow (padrão true) acrescenta os tokens de saída acumulados do turno em andamento ao cronômetro ao vivo do indicador de atividade da CLI:
⚡ Reading cli.py ( 2.3s · ↓ 1.2k tok)
A contagem é por turno (os totais da sessão são zerados como base no início do turno) e é atualizada conforme cada chamada de API do turno informa o uso. Nada aparece antes do primeiro relatório de uso chegar, então você nunca vê um enganoso ↓ 0 tok.
As duas chaves são só de exibição e só da CLI: ficam suprimidas no modo silencioso, quando display.tool_progress está em off, em execuções de consulta única/-Q em lote e nas superfícies de gateway/mensagens (essas usam o display.runtime_footer). Coloque false em qualquer uma das chaves para desligar.
Verificador de mutação de arquivo
Quando display.file_mutation_verifier é true (padrão), o Hermes acrescenta um aviso de uma linha à resposta final do assistente sempre que uma chamada de write_file ou de patch falhou durante o turno e nunca foi substituída por uma escrita bem-sucedida no mesmo caminho. Isso pega a classe de exagero do tipo "lote de correções em paralelo, metade falha em silêncio, o modelo resume como sucesso" sem você ter que rodar git status na mão depois de cada edição.
Exemplo de rodapé:
⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.
• concepts/automatic-organization.md — [patch] Could not find match for old_string
• concepts/lora.md — [patch] Could not find match for old_string
• concepts/rag-pipeline.md — [patch] Could not find match for old_string
Coloque file_mutation_verifier: false (ou HERMES_FILE_MUTATION_VERIFIER=0) para suprimir o rodapé. O verificador só dispara quando existem falhas reais pendentes no fim do turno — um modelo que tenta de novo uma correção que falhou e consegue dentro do mesmo turno não vai disparar o aviso para aquele arquivo.
Confie no verificador, não no resumo do modelo. O rodapé significa que os arquivos listados não foram alterados no disco, mesmo que a mensagem final do assistente diga que a tarefa está pronta. Causas comuns:
- Escrita negada — o caminho está na lista de bloqueio de credenciais ou fora do
HERMES_WRITE_SAFE_ROOT(veja Segurança na escrita de arquivos) - Correção que não casa — o
old_stringnão bateu com o arquivo no disco - Trava de sintaxe — o conteúdo candidato não passou na validação de JSON/YAML/TOML antes da escrita
Exemplo de rodapé quando as escritas são bloqueadas:
⚠️ File-mutation verifier: 2 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.
• ~/.hermes/cron/jobs.json — [patch] Write denied: '…' is outside HERMES_WRITE_SAFE_ROOT (/caminho/do/projeto)
• ~/.hermes/scripts/monitor.py — [write_file] Write denied: '…' is outside HERMES_WRITE_SAFE_ROOT (/caminho/do/projeto)
Se escritas no estado do Hermes (tarefas de cron, skills, scripts sob ~/.hermes/) estão falhando, confira se HERMES_WRITE_SAFE_ROOT está definida no seu ambiente. Para mudanças de cron, use a ferramenta cronjob ou o hermes cron edit em vez de corrigir o jobs.json direto.
Idioma da interface nas mensagens estáticas
A configuração display.language traduz um pequeno conjunto de mensagens estáticas voltadas ao usuário — o pedido de aprovação da CLI e um punhado de respostas de comando de barra do gateway (por exemplo, avisos de drenagem para reinício, "approval expired", "goal cleared"). Ela não traduz as respostas do agente, as linhas de log, a saída das ferramentas, os rastreamentos de erro nem as descrições dos comandos de barra — isso tudo fica em inglês. Se você quer que o agente responda em outro idioma, é só pedir no seu prompt ou na mensagem de sistema.
Valores suportados: en (padrão), zh (chinês simplificado), zh-hant (chinês tradicional), ja (japonês), de (alemão), es (espanhol), fr (francês), tr (turco), uk (ucraniano), af (africâner), ko (coreano), it (italiano), ga (irlandês), pt (português), ru (russo), hu (húngaro). Valores desconhecidos caem para o inglês.
Você também pode definir isso por sessão, com a variável de ambiente HERMES_LANGUAGE, que sobrescreve o valor da configuração.
display:
language: zh # os pedidos de aprovação da CLI aparecem em chinês
| Modo | O que você vê |
|---|---|
off | Silêncio — só a resposta final |
new | Indicador de ferramenta só quando a ferramenta muda |
all | Toda chamada de ferramenta, com uma prévia curta (padrão) |
verbose | Argumentos completos, resultados e logs de depuração |
Na CLI, alterne entre esses modos com /verbose. Para usar /verbose nas plataformas de mensagem (Telegram, Discord, Slack etc.), defina tool_progress_command: true na seção display acima. O comando então alterna o modo e salva na configuração.
O progresso de ferramenta exige um adaptador de gateway capaz de exibir atualizações de progresso com segurança. Plataformas sem suporte a edição de mensagem, incluindo o Signal, suprimem as bolhas de progresso de ferramenta mesmo que o /verbose salve um modo diferente de off.
Visão de foco (/focus, CLI + TUI)
display.focus_view: true liga a visão de foco — um modo de exibição de saída reduzida, para quando você quer a resposta, não o lance a lance. É uma camada fina sobre o mesmo maquinário de tool_progress, não um segundo caminho de supressão:
- ligá-la fixa
tool_progressemoffe guarda o seu modo anterior emdisplay.focus_saved_tool_progress; /focus offrestaura aquele modo exatamente, então uma configuração/verbose verbosesobrevive à ida e volta;- cada turno concluído termina com uma linha apagada de recuperação —
⋯ 7 tool lines hidden · /focus off to show— contada em relação ao seu modo de antes do foco, então ela nunca diz ter escondido linhas que você já tinha desligado; - uma marca
◉ focusfica permanente na barra de status (tanto na CLI de prompt_toolkit quanto na TUI de Ink), para o modo reduzido nunca ficar invisível; - alternar
/verbosecom o foco ligado devolve o controle do modo ao/verbosee apaga a marca.
A visão de foco é só de exibição. Ela nunca edita o histórico da conversa, o prompt de sistema, os esquemas de ferramenta nem qualquer carga de requisição — o detalhe escondido é suprimido na tela, nunca descartado, e o cache de prompt não é afetado em nada.
Rodapé de metadados de execução (só no gateway)
Quando display.runtime_footer.enabled: true, o Hermes acrescenta um pequeno rodapé de contexto de execução à mensagem final de cada turno do gateway. O rodapé atual pode mostrar o modelo, a porcentagem da janela de contexto e o diretório de trabalho atual. Vem desligado; ligue por gateway se o seu time quiser essa procedência em toda resposta.
display:
runtime_footer:
enabled: true
fields: ["model", "context_pct", "cwd"] # a ordem mostrada; tire qualquer um para escondê-lo
Campos suportados:
| Campo | O que mostra | Exemplo |
|---|---|---|
model | Id puro do modelo, sem o prefixo do fornecedor | gpt-5.4 |
context_pct | Ocupação de contexto da última chamada, em porcentagem | 5% |
latency | Duração do turno em tempo de relógio | 22s, 1m05s |
cwd | Diretório de trabalho relativo ao home | ~ |
O conjunto padrão de campos é ["model", "context_pct", "cwd"]. O latency é opcional — acrescente-o a fields para usar. Campos cujos dados não estão disponíveis são pulados em silêncio, em vez de aparecer como espaço vazio.
O comando de barra /footer alterna isso em tempo de execução, em qualquer sessão.
Exemplo de rodapé acrescentado a uma resposta no Telegram/Discord/Slack:
— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042
Só a mensagem final de um turno recebe o rodapé; as atualizações intermediárias ficam limpas.
Sobrescritas de progresso por plataforma
Plataformas diferentes têm necessidades diferentes de verbosidade. Use display.platforms para definir modos por plataforma:
display:
tool_progress: all # padrão global
platforms:
signal:
tool_progress: 'off' # o Signal hoje não consegue exibir bolhas de progresso de ferramenta
telegram:
tool_progress: verbose # progresso detalhado no Telegram
slack:
tool_progress: 'off' # quieto no espaço de trabalho compartilhado do Slack
Plataformas sem sobrescrita caem no valor global de tool_progress. Chaves de plataforma válidas: telegram, discord, slack, signal, whatsapp, matrix, mattermost, email, sms, homeassistant, dingtalk, feishu, wecom, weixin, bluebubbles, qqbot. A chave antiga display.tool_progress_overrides ainda é carregada por compatibilidade, mas está obsoleta e é migrada para display.platforms na primeira carga.
O Signal está listado como chave de plataforma válida porque a configuração pode ser salva por plataforma, mas o adaptador atual do Signal não consegue editar mensagens já enviadas e não desenha bolhas de progresso de ferramenta. Mantenha o tool_progress do Signal em off; use a CLI ou uma plataforma de mensagens capaz de editar se você precisa acompanhar cada chamada de ferramenta ao vivo.
interim_assistant_messages existe só no gateway. Quando ligado, o Hermes manda as atualizações do assistente concluídas no meio do turno como mensagens de chat separadas. Isso é independente de tool_progress e não exige streaming no gateway.
show_commentary (padrão true) controla o canal de comentário dos modelos Codex Responses — a narração polida de progresso que esses modelos produzem junto com o raciocínio privado. Quando ligado, cada mensagem de comentário concluída é entregue como uma atualização visível no meio do turno (no gateway, isso também exige interim_assistant_messages). Coloque false se a narração extra te incomoda: aí o comentário cai para o canal de raciocínio e só aparece quando show_reasoning está ligado.
Privacidade
privacy:
redact_pii: false # Remove dados pessoais do contexto do LLM (só no gateway)
Quando redact_pii é true, o gateway oculta informações de identificação pessoal do prompt de sistema antes de mandar para o LLM nas plataformas suportadas:
| Campo | Tratamento |
|---|---|
| Números de telefone (ID de usuário no WhatsApp/Signal) | Viram hash user_<sha256 de 12 caracteres> |
| IDs de usuário | Viram hash user_<sha256 de 12 caracteres> |
| IDs de conversa | A parte numérica vira hash, o prefixo da plataforma é preservado (telegram:<hash>) |
| IDs de canal principal | A parte numérica vira hash |
| Nomes / nomes de usuário | Não são afetados (escolhidos pela pessoa, visíveis publicamente) |
Suporte por plataforma: a ocultação vale para WhatsApp, Signal e Telegram. Discord e Slack ficam de fora porque os sistemas de menção deles (<@user_id>) precisam do ID real no contexto do LLM.
Os hashes são determinísticos — o mesmo usuário sempre vira o mesmo hash, então o modelo ainda consegue distinguir pessoas em conversas de grupo. O roteamento e a entrega usam os valores originais internamente.
Identidade das requisições do Codex da OpenAI
A OpenAI exige que arcabouços Codex de terceiros se identifiquem.
Requisições autenticadas por ChatGPT ao endpoint oficial do Codex mandam
automaticamente originator: hermes-agent e User-Agent: HermesAgent/<version>.
O cabeçalho existente da conta do ChatGPT é preservado. Nenhum conteúdo extra de prompt
nem requisição de telemetria é enviado.
Requisições diretas à API da OpenAI e endpoints de proxy personalizados ficam inalterados.
Fala para texto (STT)
stt:
enabled: true # Transcreve automaticamente as mensagens de voz que chegam (padrão: true)
echo_transcripts: true # Publica as transcrições cruas de volta na conversa como 🎙️ "..." (padrão: true)
provider: "local" # "local" | "groq" | "openai" | "mistral" | "xai" | "elevenlabs" | "deepinfra" | ...
language: "en" # Dica GLOBAL de idioma para todo provedor (o idioma por provedor vence); coloque "" para detecção automática
cloud_trim_silence: true # corta pausas longas com ffmpeg antes de enviar a um provedor na nuvem (padrão: true)
cloud_trim_threshold_db: -40 # áudio mais baixo que isso conta como silêncio
cloud_trim_keep_ms: 300 # quanto de cada pausa sobrevive ao corte (mantém o ritmo natural)
# prompt: "Hermes, Teknium, Nous Research, kanban" # Dica estática de vocabulário (veja abaixo)
local:
model: "base" # tiny, base, small, medium, large-v3
language: "" # sobrescrita, por provedor, do stt.language
initial_prompt: "" # prompt opcional do whisper para enviesar vocabulário/escrita (por exemplo, chinês simplificado)
vad: true # Filtro Silero VAD (ligado por padrão) — o silêncio nunca chega ao whisper; false = comportamento cru (música/ambiente)
vad_min_silence_ms: 500 # silêncio mínimo (ms) que separa os pedaços de fala quando o vad está ligado
no_speech_prob_threshold: 0.6 # descarta um segmento só quando no_speech_prob > isto...
logprob_threshold: -1.0 # ...E avg_logprob < isto (os dois têm que bater — fala real e baixinha sobrevive)
unload_after_idle_seconds: 0 # 0=nunca descarrega (padrão); por exemplo, 300 = libera o modelo depois de 5min parado
groq:
language: "" # sobrescrita, por provedor, do stt.language
openai:
model: "whisper-1" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe | gpt-transcribe
language: "" # sobrescrita, por provedor, do stt.language
# model: "whisper-1" # Chave antiga de reserva, ainda respeitada
A resolução de idioma é a mesma para todo provedor de STT (local, groq, openai, mistral, xai, elevenlabs, deepinfra, provedores de comando e plugins): stt.<provider>.language → stt.language → variável de ambiente HERMES_LOCAL_STT_LANGUAGE → detecção automática do provedor. O padrão é stt.language: "en" — a detecção automática do Whisper erra com frequência em trechos curtos ou com sotaque, o que aparece como notas de voz transcritas no idioma errado. Quem não fala inglês deve definir stt.language com o próprio código de idioma uma vez (por exemplo, "es", "zh", "uk"); coloque "" para voltar à detecção automática em uso multilíngue.
Coloque stt.echo_transcripts: false quando o gateway deve transcrever as notas de voz para o agente mas não pode publicar a transcrição crua de volta na conversa (por exemplo, bots de WhatsApp voltados ao cliente).
Comportamento por provedor:
localusa ofaster-whisperrodando na sua máquina. Instale à parte compip install faster-whisper. O endurecimento contra alucinação em silêncio vem ligado: um filtro Silero VAD impede que silêncio/ruído cheguem ao Whisper, o condicionamento entre janelas fica desligado e os segmentos que o próprio modelo marca como provavelmente-não-fala e de baixa confiança são descartados. Coloquestt.local.vad: falsepara transcrever áudio que não é fala (música, ambiente) com o comportamento cru. O modelo fica carregado na memória entre mensagens de voz, para transcrição de baixa latência; definastt.local.unload_after_idle_seconds(por exemplo,300para 5 minutos) para liberar o modelo automaticamente quando ficar parado. Isso libera memória de GPU em máquinas com CUDA (o ganho principal quando um LLM local divide a GPU); na CPU, a memória volta a ser reaproveitável pelo processo, embora o consumo visível ao sistema possa não encolher até o processo precisar do espaço para outra coisa. A próxima mensagem de voz recarrega o modelo de forma transparente.groqusa o endpoint compatível com Whisper da Groq e lêGROQ_API_KEY. Passestt.groq.language(ou a variável de ambiente globalHERMES_LOCAL_STT_LANGUAGE) para pular a detecção automática e reduzir a latência.openaiusa a API de fala da OpenAI e lêVOICE_TOOLS_OPENAI_KEY.
Os provedores na nuvem (groq, openai, mistral, xai, elevenlabs, deepinfra) recebem, por padrão, um corte de silêncio antes do envio quando o ffmpeg está instalado: pausas longas numa nota de voz são encolhidas no cliente antes de o arquivo subir, guardando cloud_trim_keep_ms de cada pausa para o ritmo natural sobreviver. Áudio mais curto significa envio mais rápido, cobrança menor por minuto de áudio e menos alucinações de silêncio vindas do modelo remoto. Trechos com menos de 12 segundos pulam o corte por completo (a economia não faria diferença, e vários provedores cobram um mínimo por requisição de qualquer jeito). O corte é de melhor esforço — se faltar o ffmpeg, se o corte falhar, se o trecho for quase todo silêncio ou se cortar economizasse menos de uns 10%, o arquivo original sobe intocado. Coloque stt.cloud_trim_silence: false para sempre enviar o original (por exemplo, ao transcrever música ou áudio ambiente por um provedor na nuvem). Provedores do tipo comando e de plugin nunca recebem áudio cortado.
Um stt.provider escolhido explicitamente é respeitado à risca — se ele não estiver disponível, a transcrição dá erro com orientação para rodar hermes tools, em vez de trocar de provedor. Só quando nenhum provedor foi escolhido em momento nenhum é que o Hermes detecta sozinho, nesta ordem: local → groq → openai.
As sobrescritas de modelo da Groq e da OpenAI vêm do ambiente:
STT_GROQ_MODEL=whisper-large-v3-turbo
STT_OPENAI_MODEL=whisper-1
GROQ_BASE_URL=https://api.groq.com/openai/v1
STT_OPENAI_BASE_URL=https://api.openai.com/v1
Prompt de transcrição (dicas de vocabulário)
stt.prompt é uma dica estática opcional, passada aos backends de STT que aceitam prompt. Use para nomes próprios, nomes de produto e jargão que os modelos da família Whisper, sem isso, ouvem errado:
stt:
provider: "local"
prompt: "Hermes, Teknium, Nous Research, kanban, Ollama"
Composição. O valor da configuração é a base. Plugins que registram o hook pre_transcription alteram em cima dele, com o último a escrever vencendo, campo a campo. As dicas de vários plugins compõem de forma determinística: a descoberta carrega os plugins em ordem alfabética de id, e os callbacks de cada plugin rodam na ordem de registro dele, então o mesmo conjunto de plugins sempre produz o mesmo prompt final. Um hook que devolve string vazia em prompt limpa o prompt da configuração naquela requisição. Os hooks também podem sobrescrever language e model; file_path é somente leitura, e qualquer tentativa de alterá-lo é registrada e descartada. Sem nenhum hook registrado e sem stt.prompt definido, a requisição enviada é idêntica à das versões anteriores.
Suporte por provedor.
| Provedor | Parâmetro de prompt | Comportamento |
|---|---|---|
local (faster-whisper) | initial_prompt | Repassado sem alteração ao modelo local |
openai | prompt | Repassado sem alteração na requisição de transcrição |
groq | prompt | Repassado sem alteração na requisição de transcrição |
mistral | prompt | Repassado sem alteração na requisição de transcrição |
deepinfra | prompt | Caminho compatível com OpenAI, repassado sem alteração |
xai | não suportado | Registrado em DEBUG; a requisição segue sem o prompt |
elevenlabs | não suportado | Registrado em DEBUG; a requisição segue sem o prompt |
local_command | não suportado | Registrado em DEBUG; a requisição segue sem o prompt |
stt.providers.<name> com type: command | não suportado | Registrado em DEBUG; a requisição segue sem o prompt |
| Provedores registrados por plugin | prompt nos kwargs de transcribe(**extra) | Só é enviado quando há um prompt definido, então provedores anteriores a esta chave veem chamadas iguais às de antes |
Tamanho. Modelos da família Whisper só se condicionam aos últimos ~224 tokens do prompt. Nos backends da família whisper (local, openai, groq, deepinfra), o Hermes aplica esse teto no cliente: um prompt final longo demais é cortado, ficando com o final, e um aviso é registrado — a requisição nunca dá erro por tamanho de prompt. Outros backends (mistral, provedores de plugin) recebem o prompt sem alteração e cuidam da própria validação. De qualquer forma, mantenha as dicas curtas e específicas.
O prompt final é mandado ao provedor de STT configurado junto com o arquivo de áudio. Não coloque segredos nem contexto derivado da sessão no stt.prompt nem em nada que um hook pre_transcription devolva, ainda mais quando o provedor é uma API hospedada em vez do faster-whisper local.
Modo de voz (CLI)
voice:
record_key: "ctrl+b" # Tecla de apertar-para-falar dentro da CLI
max_recording_seconds: 120 # Parada dura para gravações longas
auto_tts: false # Liga as respostas faladas automaticamente quando /voice on
beep_enabled: true # Toca bipes de início/fim de gravação no modo de voz da CLI
beep_volume: 0.3 # Amplitude do bipe (0.0-1.0); aumente em sistemas silenciosos / fones
silence_threshold: 200 # Limiar de RMS para detecção de fala
silence_duration: 3.0 # Segundos de silêncio antes da parada automática
Use /voice on na CLI para ligar o modo de microfone, a record_key para começar/parar a gravação e /voice tts para alternar as respostas faladas. Veja Modo de voz para a configuração de ponta a ponta e o comportamento específico de cada plataforma.
Streaming
Manda os tokens para o terminal ou para as plataformas de mensagem conforme eles chegam, em vez de esperar a resposta inteira.
Streaming na CLI
display:
streaming: true # Manda os tokens para o terminal em tempo real
show_reasoning: true # Também transmite os tokens de raciocínio/pensamento (opcional)
Quando ligado, as respostas aparecem token a token dentro de uma caixa de streaming. As chamadas de ferramenta continuam sendo capturadas em silêncio. Se o provedor não suportar streaming, o Hermes cai para a exibição normal automaticamente.
Streaming no gateway (Telegram, Discord, Slack)
streaming:
enabled: true # Liga a edição progressiva de mensagem (padrão: false)
transport: auto # "auto" (padrão) | "edit" (edição progressiva de mensagem) | "off"
edit_interval: 0.8 # Segundos entre edições de mensagem (padrão: 0.8)
buffer_threshold: 24 # Caracteres antes de forçar uma descarga de edição (padrão: 24)
cursor: " ▉" # Cursor mostrado durante o streaming
fresh_final_after_seconds: 0 # Opta pela mensagem final nova (Telegram) quando a prévia tem esta idade
Quando ligado, o bot manda uma mensagem no primeiro token e vai editando conforme mais tokens chegam. Plataformas que não suportam edição de mensagem (Signal, e-mail, Home Assistant) são detectadas na primeira tentativa — o streaming é desligado com elegância naquela sessão, sem enxurrada de mensagens.
Para atualizações naturais do assistente no meio do turno, separadas e sem edição progressiva de token, defina display.interim_assistant_messages: true.
Tratamento de estouro: se o texto transmitido passar do limite de tamanho de mensagem da plataforma (cerca de 4096 caracteres), a mensagem atual é finalizada e uma nova começa automaticamente.
Mensagem final nova (Telegram): o editMessageText do Telegram preserva a marca de tempo original da mensagem, então uma resposta transmitida demorada ficaria com a marca de tempo do primeiro token mesmo depois de terminar. Coloque fresh_final_after_seconds > 0 para optar por entregar prévias antigas como mensagens finais novinhas, com exclusão da prévia por melhor esforço. O padrão é 0, que sempre finaliza as respostas transmitidas no lugar e evita a breve sequência de mensagem duplicada/exclusão nos clientes que mostram as duas operações.
O interruptor mestre streaming.enabled é false por padrão — nada é transmitido até você virá-lo. Uma vez ligado, o streaming é decidido por plataforma: o Telegram já vem com display.platforms.telegram.streaming: true (transmite) e o Discord com display.platforms.discord.streaming: false (não transmite). Então, depois de ligar o streaming, o Telegram transmite de cara e o Discord continua com respostas de mensagem inteira até você mudar o interruptor dele. Você pode ajustar esses interruptores por plataforma pelos botões de Channels no painel ou direto no ~/.hermes/config.yaml.
Isolamento de sessão em conversas de grupo
Limite quantas sessões de chat podem ficar abertas ao mesmo tempo entre CLI, TUI/painel e gateway de mensagens:
max_concurrent_sessions: null # null/0 = ilimitado; inteiro positivo = teto de sessões ativas
Uma vaga é ocupada quando uma sessão roda o primeiro turno, não quando uma janela de chat é aberta. Abrir, retomar ou reconectar a uma conversa não custa nada até você mandar uma mensagem, então abas de desktop paradas (e as retomadas em segundo plano que um websocket instável dispara) não conseguem matar de fome o gateway de mensagens que divide esse teto.
Quando o teto é atingido, o Hermes devolve uma mensagem direta de limite dizendo quais
superfícies estão com as vagas. As sessões ativas existentes mantêm o comportamento normal.
Rode hermes status para ver o uso atual de vagas e todos os ocupantes.
A chave canônica é a de nível superior max_concurrent_sessions. O Hermes também aceita
gateway.max_concurrent_sessions como reserva, mas a chave de nível superior vence quando
as duas estão definidas.
O teto é aplicado com um arquivo local de arrendamento em tempo de execução e é de melhor esforço: o Hermes
falha aberto se o registro não puder ser lido ou travado, para os usuários não ficarem na mão.
Foi pensado para um runtime de um host/perfil só, não para um $HERMES_HOME compartilhado
montado em várias máquinas.
Controle se as conversas compartilhadas mantêm uma conversa por sala ou uma conversa por participante:
group_sessions_per_user: true # true = isolamento por usuário em grupos/canais, false = uma sessão compartilhada por conversa
trueé o padrão e a configuração recomendada. Em canais do Discord, grupos do Telegram, canais do Slack e contextos compartilhados parecidos, cada remetente ganha a própria sessão quando a plataforma fornece um ID de usuário.falsevolta ao comportamento antigo, de sala compartilhada. Isso pode ser útil se você quer mesmo que o Hermes trate um canal como uma conversa colaborativa única, mas também significa que as pessoas dividem contexto, custo de token e estado de interrupção.- Mensagens diretas não são afetadas. O Hermes continua indexando DMs pelo ID de conversa/DM, como sempre.
- Threads ficam isoladas do canal pai dos dois jeitos; com
true, cada participante também ganha a própria sessão dentro da thread.
Para os detalhes de comportamento e exemplos, veja Sessões e o guia do Discord.
Comportamento em DM não autorizada
Controle o que o Hermes faz quando alguém desconhecido manda uma mensagem direta:
unauthorized_dm_behavior: pair
whatsapp:
unauthorized_dm_behavior: ignore
pairé o padrão nas plataformas de DM em estilo de conversa. O Hermes nega o acesso, mas responde na DM com um código de pareamento de uso único.ignoredescarta as DMs não autorizadas em silêncio.- O e-mail tem
ignorecomo padrão, a menos queplatforms.email.unauthorized_dm_behavior: pairesteja definido, porque caixas de entrada podem ter mensagens não lidas sem relação nenhuma. - As seções de plataforma sobrescrevem o padrão global, então dá para manter o pareamento ligado no geral e deixar uma plataforma mais quieta.
Comandos rápidos
Defina comandos personalizados que rodam comandos de shell sem envolver o LLM, ou que apelidam um comando de barra para outro. Comandos rápidos do tipo exec custam zero token e são úteis a partir de plataformas de mensagem (Telegram, Discord etc.) para checagens rápidas de servidor ou scripts utilitários.
quick_commands:
status:
type: exec
command: systemctl status hermes-agent
disk:
type: exec
command: df -h /
update:
type: exec
command: cd ~/.hermes/hermes-agent && git pull && uv pip install -e .
gpu:
type: exec
command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader
restart:
type: alias
target: /gateway restart
Uso: digite /status, /disk, /update, /gpu ou /restart na CLI ou em qualquer plataforma de mensagens. Os comandos exec rodam localmente no host e devolvem a saída direto — sem chamada de LLM, sem gastar token. Os comandos alias reescrevem para o comando de barra configurado como alvo.
- Tempo limite de 30 segundos — comandos demorados são mortos com uma mensagem de erro
- Prioridade — os comandos rápidos são checados antes dos comandos de skill, então você pode sobrescrever nomes de skill
- Autocompletar — os comandos rápidos são resolvidos na hora do despacho e não aparecem nas tabelas embutidas de autocompletar de comando de barra
- Tipo — os tipos suportados são
execealias; outros tipos mostram um erro - Funciona em todo lugar — CLI, Telegram, Discord, Slack, WhatsApp, Signal, e-mail, Home Assistant
Atalhos de prompt que são só uma string não valem como comando rápido. Para fluxos de prompt reutilizáveis, crie uma skill ou um alias para um comando de barra que já existe.
Atraso humano
Simule um ritmo de resposta parecido com o humano nas plataformas de mensagem:
human_delay:
mode: "off" # off | natural | custom
min_ms: 800 # Atraso mínimo (modo custom)
max_ms: 2500 # Atraso máximo (modo custom)
Execução de código
Configure a ferramenta execute_code:
code_execution:
mode: project # project (padrão) | strict
timeout: 300 # Tempo máximo de execução, em segundos
max_tool_calls: 50 # Máximo de chamadas de ferramenta dentro da execução de código
mode controla o diretório de trabalho e o interpretador Python dos scripts:
project(padrão) — os scripts rodam no diretório de trabalho da sessão, com o python do virtualenv/conda env ativo. As dependências do projeto (pandas,torch, pacotes do projeto) e os caminhos relativos (.env,./data.csv) resolvem naturalmente, igual ao que oterminal()enxerga.strict— os scripts rodam num diretório temporário de preparo, comsys.executable(o python do próprio Hermes). Reprodutibilidade máxima, mas as dependências do projeto e os caminhos relativos não resolvem.
A limpeza de ambiente (remove *_API_KEY, *_TOKEN, *_SECRET, *_PASSWORD, *_CREDENTIAL, *_PASSWD, *_AUTH) e a lista de ferramentas permitidas valem igualmente nos dois modos — trocar de modo não muda a postura de segurança.
Backends de busca na web
As ferramentas web_search e web_extract suportam cinco provedores de backend. Configure o backend no config.yaml ou pelo hermes tools:
web:
backend: firecrawl # firecrawl | searxng | parallel | tavily | exa
# Ou use chaves por capacidade para misturar provedores (por exemplo, busca grátis + extração paga):
search_backend: "searxng"
extract_backend: "firecrawl"
# Reserva grátis, sem chave (padrão: true). Sem backend configurado
# e sem chaves de API, as ferramentas de web se revezam entre as camadas
# gratuitas de Exa/Parallel/Tavily/Firecrawl/Keenable. Coloque false para desligar.
keyless_fallback: true
# Resgate sem chave de uma vez só (padrão: true). Quando o backend escolhido/com chave
# falha numa chamada, aquela chamada é repetida no anel sem chave; a próxima
# chamada tenta o backend escolhido de novo (nunca fica grudado).
keyless_rescue: true
# Fixa Exa/Parallel numa camada (definido pelas linhas Free/Paid do hermes tools).
# free = sempre o endpoint anônimo; paid = sempre o caminho do SDK com chave;
# sem definir = automático (com chave -> paid, senão free).
provider_tier:
parallel: free
exa: paid
| Backend | Variável de ambiente | Busca | Extração |
|---|---|---|---|
| Firecrawl (padrão) | FIRECRAWL_API_KEY | ✔ | ✔ |
| SearXNG | SEARXNG_URL | ✔ | — |
| Parallel | PARALLEL_API_KEY (opcional — camada grátis sem chave) | ✔ | ✔ |
| Tavily | TAVILY_API_KEY (opcional — sem chave quando escolhido) | ✔ | ✔ |
| Exa | EXA_API_KEY (opcional — camada grátis sem chave) | ✔ | ✔ |
Escolha do backend: o runtime sempre usa a escolha guardada em web.backend (definida pelo hermes tools; nous roteia pelo Tool Gateway gerenciado). Só quando nenhum backend de web foi escolhido em momento nenhum é que um é detectado automaticamente pelas chaves de API disponíveis: se só SEARXNG_URL está definida, usa-se o SearXNG; se só EXA_API_KEY, o Exa; se só TAVILY_API_KEY, o Tavily; se só PARALLEL_API_KEY, o Parallel; se só KEENABLE_API_KEY, o Keenable. Sem escolha nenhuma e sem credencial nenhuma, as requisições se revezam em rodízio pelo anel grátis sem chave (Exa / Parallel / Tavily / Firecrawl / Keenable), com troca automática para o próximo da fila em limites de taxa — veja o guia de Busca na web para os detalhes. Depois que existe uma escolha, acrescentar uma chave ao .env não muda a rota. Escolher Tavily, Firecrawl ou Keenable no hermes tools também funciona sem chave.
O SearXNG é um metabuscador grátis, auto-hospedado e que respeita a privacidade, consultando mais de 70 buscadores. Não precisa de chave de API — é só apontar SEARXNG_URL para a sua instância (por exemplo, http://localhost:8080). O SearXNG só busca; o web_extract exige um provedor de extração separado (defina web.extract_backend). Veja o guia de configuração da busca na web para as instruções de instalação com Docker.
Firecrawl auto-hospedado: defina FIRECRAWL_API_URL apontando para a sua instância. Com uma URL personalizada, a chave de API vira opcional (defina `USE_DB_AUTHENTICATION=*** no servidor para desligar a autenticação).
Modos de busca do Parallel: defina PARALLEL_SEARCH_MODE para controlar o comportamento da busca — fast, one-shot ou agentic (padrão: agentic).
Exa: defina EXA_API_KEY no ~/.hermes/.env. Suporta filtro por category (company, research paper, news, people, personal site, pdf) e filtros de domínio/data.
Navegador
Configure o comportamento da automação de navegador:
browser:
inactivity_timeout: 120 # Segundos antes de fechar sozinho as sessões paradas
command_timeout: 30 # Tempo limite, em segundos, dos comandos de navegador (captura de tela, navegação etc.)
record_sessions: false # Grava automaticamente as sessões de navegador como vídeos WebM em ~/.hermes/browser_recordings/
# Sobrescrita opcional de CDP — quando definida, o Hermes se conecta direto ao seu
# navegador da família Chromium (por /browser connect) em vez de subir um navegador headless.
cdp_url: ""
# Supervisor de diálogos — controla como os diálogos nativos de JS (alert / confirm / prompt)
# são tratados quando há um backend CDP conectado (Browserbase, navegador local da família
# Chromium por /browser connect). Ignorado no Camofox e no modo local padrão do agent-browser.
dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept
dialog_timeout_s: 300 # Descarte automático de segurança no must_respond (segundos)
camofox:
managed_persistence: false # Quando true, as sessões do Camofox mantêm cookies/logins entre reinícios
user_id: "" # userId do Camofox gerenciado externamente, opcional
session_key: "" # Chave de sessão opcional enviada quando o Hermes cria uma aba
adopt_existing_tab: false # Reaproveita uma aba existente desta identidade antes de criar outra
Políticas de diálogo:
must_respond(padrão) — captura o diálogo, mostra embrowser_snapshot.pending_dialogse espera o agente chamarbrowser_dialog(action=...). Depois dedialog_timeout_ssegundos sem resposta, o diálogo é descartado automaticamente, para a thread de JS da página não travar para sempre.auto_dismiss— captura e descarta na hora. O agente ainda vê o registro do diálogo embrowser_snapshot.recent_dialogs, comclosed_by="auto_policy", depois do fato.auto_accept— captura e aceita na hora. Útil em páginas com prompts agressivos debeforeunload.
Veja a página do recurso de navegador para o fluxo completo de diálogo.
O conjunto de ferramentas de navegador suporta vários provedores. Veja a página do recurso de navegador para os detalhes de Browserbase, Browser Use e da configuração local por CDP com navegadores da família Chromium.
Fuso horário
Sobrescreva o fuso horário local do servidor com uma string de fuso IANA. Afeta as marcas de tempo nos logs, o agendamento do cron e a injeção de hora no prompt de sistema.
timezone: "America/New_York" # Fuso IANA (padrão: "" = hora local do servidor)
Valores suportados: qualquer identificador de fuso IANA (por exemplo, America/New_York, Europe/London, Asia/Kolkata, UTC). Deixe vazio ou omita para a hora local do servidor.
Discord
Configure o comportamento específico do Discord no gateway de mensagens:
discord:
require_mention: true # Exige @menção para responder em canais de servidor
free_response_channels: "" # IDs de canal, separados por vírgula, onde o bot responde sem @menção
auto_thread: true # Cria threads automaticamente ao ser mencionado em canais
require_mention— quandotrue(padrão), o bot só responde em canais de servidor quando é mencionado com@NomeDoBot. As DMs sempre funcionam sem menção.free_response_channels— lista de IDs de canal, separados por vírgula, onde o bot responde a toda mensagem sem exigir menção.auto_thread— quandotrue(padrão), as menções em canais criam automaticamente uma thread para a conversa, mantendo os canais limpos (parecido com as threads do Slack).
Segurança
Varredura de segurança antes da execução e ocultação de segredos:
security:
redact_secrets: true # Oculta padrões de chave de API na saída de ferramenta e nos logs (ligado por padrão)
tirith_enabled: true # Liga a varredura de segurança Tirith nos comandos de terminal
tirith_path: "tirith" # Caminho do binário do tirith (padrão: "tirith" no $PATH)
tirith_timeout: 5 # Segundos de espera pela varredura do tirith antes de estourar o tempo
tirith_fail_open: true # Permite executar o comando se o tirith não estiver disponível
website_blocklist: # Veja a seção Lista de bloqueio de sites abaixo
enabled: false
domains: []
shared_files: []
redact_secrets— quandotrue, detecta e oculta automaticamente padrões que parecem chaves de API, tokens e senhas na saída de ferramenta, antes de ela entrar no contexto da conversa e nos logs. Ligado por padrão. Coloquefalseexplicitamente só quando você precisa das strings cruas parecidas com credencial para depurar ou desenvolver o ocultador.tirith_enabled— quandotrue, os comandos de terminal são varridos pelo Tirith antes de executar, para detectar operações potencialmente perigosas.tirith_path— caminho do binário do tirith. Defina se o tirith estiver instalado num lugar fora do padrão.tirith_timeout— máximo de segundos a esperar por uma varredura do tirith. Os comandos seguem se a varredura estourar o tempo.tirith_fail_open— quandotrue(padrão), os comandos podem executar se o tirith não estiver disponível ou falhar. Coloquefalsepara bloquear os comandos quando o tirith não conseguir verificá-los.
Lista de bloqueio de sites
Bloqueie domínios específicos para as ferramentas de web e de navegador do agente:
security:
website_blocklist:
enabled: false # Liga o bloqueio de URL (padrão: false)
domains: # Lista de padrões de domínio bloqueados
- "*.internal.company.com"
- "admin.example.com"
- "*.local"
shared_files: # Carrega regras adicionais de arquivos externos
- "/etc/hermes/blocked-sites.txt"
Quando ligado, qualquer URL que case com um padrão de domínio bloqueado é recusada antes de a ferramenta de web ou de navegador executar. Vale para web_search, web_extract, browser_navigate e qualquer ferramenta que acesse URLs.
As regras de domínio suportam:
- Domínios exatos:
admin.example.com - Curingas de subdomínio:
*.internal.company.com(bloqueia todos os subdomínios) - Curingas de TLD:
*.local
Os arquivos compartilhados têm uma regra de domínio por linha (linhas em branco e comentários com # são ignorados). Arquivos faltando ou ilegíveis registram um aviso, mas não desligam as outras ferramentas de web.
A política fica em cache por 30 segundos, então as mudanças de configuração passam a valer rápido, sem reiniciar.
Aprovações inteligentes
Controle como o Hermes lida com comandos potencialmente perigosos:
approvals:
mode: smart # smart | manual | off
| Modo | Comportamento |
|---|---|
smart (padrão) | Usa um LLM auxiliar para avaliar se um comando sinalizado é mesmo perigoso. Comandos de baixo risco são aprovados sozinhos, só para aquele comando. Comandos realmente arriscados são negados; decisões incertas sobem para o usuário. |
manual | Pergunta ao usuário antes de executar qualquer comando sinalizado. Na CLI, mostra um diálogo interativo de aprovação. Em mensagens, coloca um pedido de aprovação na fila. |
off | Pula todas as checagens de aprovação. Equivale a HERMES_YOLO_MODE=true. Use com cuidado. |
O modo smart é especialmente útil para reduzir o cansaço de aprovação — deixa o agente trabalhar com mais autonomia nas operações seguras e ainda assim pega os comandos realmente destrutivos.
Colocar approvals.mode: off desliga todas as checagens de segurança dos comandos de terminal. Use isso só em ambientes confiáveis e isolados.
Disjuntor de negações
approvals.denial_breaker_threshold (padrão 3) protege contra o agente ficar tentando variações de um comando que o revisor de aprovação inteligente insiste em negar — cada tentativa queima mais uma chamada de LLM guardião. Depois dessa quantidade de negações seguidas numa sessão, a mensagem de negação vira uma instrução de parada dura, dizendo ao agente para parar, relatar a operação bloqueada e pedir que você a rode na mão ou use /approve. Qualquer aprovação zera a contagem; coloque 0 para desligar:
approvals:
denial_breaker_threshold: 3 # 0 desliga o disjuntor
Regras de negação
approvals.deny é uma lista de padrões glob que bloqueiam incondicionalmente os comandos de terminal que casarem — até com --yolo, /yolo ou mode: off. É a contraparte editável pelo usuário da lista de bloqueio linha-dura embutida:
approvals:
deny:
- "git push --force*"
- "*curl*|*sh*"
Os padrões são globs de fnmatch que não diferenciam maiúsculas e precisam estar entre aspas no YAML (um * solto no começo é erro de análise). Veja Segurança — regras de negação definidas pelo usuário para os detalhes.
Política personalizada de aprovação inteligente
approvals.smart_policy deixa você acrescentar as suas próprias regras às instruções do revisor de aprovação inteligente. Quando definido, o texto é acrescentado ao prompt de sistema do LLM guardião (o canal confiável — nunca junto do texto não confiável do comando), então você pode apertar ou afrouxar o julgamento dele para o seu ambiente sem editar código:
approvals:
smart_policy: |
Always ESCALATE commands that modify anything under /etc.
APPROVE docker compose restarts in ~/deploys — they are routine here.
Checkpoints
Instantâneos automáticos do sistema de arquivos antes de operações destrutivas em arquivo. Veja Checkpoints e rollback para os detalhes.
checkpoints:
enabled: false # Liga os checkpoints automáticos (também: hermes chat --checkpoints). Padrão: false (opcional).
max_snapshots: 20 # Máximo de checkpoints a manter por diretório (padrão: 20)
Delegação
Configure o comportamento dos subagentes da ferramenta de delegação:
delegation:
# model: "google/gemini-3-flash-preview" # Sobrescreve o modelo (vazio = herda do pai)
# provider: "openrouter" # Sobrescreve o provedor (vazio = herda do pai)
# base_url: "http://localhost:1234/v1" # Endpoint direto compatível com OpenAI (tem precedência sobre o provedor)
# api_key: "local-key" # Chave de API do base_url (cai para OPENAI_API_KEY)
# api_mode: "" # Protocolo de fio do base_url: "chat_completions", "codex_responses" ou "anthropic_messages". Vazio = detecta pela URL (por exemplo, sufixo /anthropic → anthropic_messages). Defina explicitamente em endpoints fora do padrão que a heurística não consegue detectar.
max_concurrent_children: 3 # Filhos em paralelo por lote (piso 1, sem teto). Também pela variável de ambiente DELEGATION_MAX_CONCURRENT_CHILDREN.
worktree_isolation: false # Dá a cada filho o próprio worktree do git, partindo do HEAD (só no backend local + repositórios git; inspirado no Muse Code). Veja Delegação para subagentes → Isolamento por worktree.
max_spawn_depth: 1 # Teto de profundidade da árvore de delegação (1-3, limitado). 1 = plana (padrão): o pai cria folhas que não podem delegar. 2 = filhos orquestradores podem criar netos folha. 3 = três níveis.
orchestrator_enabled: true # Interruptor geral. Quando false, role="orchestrator" é ignorado e todo filho vira folha à força, seja qual for o max_spawn_depth.
Sobrescrita de provedor:modelo dos subagentes: por padrão, os subagentes herdam o provedor e o modelo do agente pai. Defina delegation.provider e delegation.model para rotear os subagentes a outro par provedor:modelo — por exemplo, usar um modelo barato/rápido em subtarefas bem delimitadas enquanto o seu agente principal roda um modelo de raciocínio caro.
Sobrescrita de endpoint direto: se você quer o caminho óbvio de endpoint personalizado, defina delegation.base_url, delegation.api_key e delegation.model. Isso manda os subagentes direto para aquele endpoint compatível com OpenAI e tem precedência sobre delegation.provider. Se delegation.api_key for omitida, o Hermes cai só para OPENAI_API_KEY.
Protocolo de fio (api_mode): o Hermes detecta o protocolo de fio pela delegation.base_url (por exemplo, caminhos terminados em /anthropic → anthropic_messages; nomes de host do Codex / Anthropic nativo / Kimi-coding mantêm a detecção que já tinham). Para endpoints que a heurística não consegue classificar — por exemplo, Azure AI Foundry, MiniMax, Zhipu GLM ou proxies LiteLLM na frente de um backend em formato Anthropic —, defina delegation.api_mode explicitamente como chat_completions, codex_responses ou anthropic_messages. Deixe vazio (o padrão) para manter a detecção automática.
O provedor de delegação usa a mesma resolução de credencial da inicialização da CLI/gateway. Todos os provedores configurados são suportados: openrouter, nous, copilot, zai, kimi-coding, minimax, minimax-cn. Quando um provedor é definido, o sistema resolve sozinho a URL base, a chave de API e o modo de API corretos — não precisa ligar credencial na mão.
Precedência: delegation.base_url na configuração → delegation.provider na configuração → provedor do pai (herdado). delegation.model na configuração → modelo do pai (herdado). Definir só model sem provider muda apenas o nome do modelo, mantendo as credenciais do pai (útil para trocar de modelo dentro do mesmo provedor, como o OpenRouter).
Largura e profundidade: max_concurrent_children limita quantos subagentes rodam em paralelo por lote (padrão 3, piso de 1, sem teto). Também pode ser definido pela variável de ambiente DELEGATION_MAX_CONCURRENT_CHILDREN. Quando o modelo manda um array tasks maior que o teto, o delegate_task devolve um erro de ferramenta explicando o limite, em vez de cortar em silêncio. max_spawn_depth controla a profundidade da árvore de delegação (limitada a 1-3). No padrão 1, a delegação é plana: os filhos não podem criar netos, e passar role="orchestrator" degrada em silêncio para leaf. Suba para 2 para os filhos orquestradores poderem criar netos folha; 3 para árvores de três níveis. O agente opta pela orquestração em cada chamada, com role="orchestrator"; orchestrator_enabled: false força todo filho de volta a folha, de qualquer jeito. O custo cresce de forma multiplicativa — em max_spawn_depth: 3 com max_concurrent_children: 3, a árvore pode chegar a 3×3×3 = 27 agentes folha simultâneos. Veja Delegação para subagentes → limite de profundidade e orquestração aninhada para os padrões de uso.
Notificações de processo dos filhos: os processos em segundo plano iniciados por subagentes roteiam as notificações de conclusão/acompanhamento para a conversa do pai, mas elas são suprimidas lá por padrão — o entregável é o resultado consolidado do filho. Defina delegation.surface_child_process_notifications: true para entregá-las (com atribuição ao subagente). Os resultados da delegação em si nunca são suprimidos. Veja Delegação para subagentes → notificações de processo em segundo plano dos filhos.
Esclarecimento
Configure quanto tempo o gateway espera por uma resposta a uma pergunta de esclarecimento. A chave canônica é agent.clarify_timeout (padrão 3600 segundos); a chave antiga de nível superior clarify.timeout ainda é respeitada se estiver definida explicitamente:
agent:
clarify_timeout: 3600 # Segundos de espera pelo esclarecimento do usuário (0 ou menos = ilimitado)
Arquivos de contexto (SOUL.md, AGENTS.md)
O Hermes usa dois escopos diferentes de contexto:
| Arquivo | Objetivo | Escopo |
|---|---|---|
SOUL.md | Identidade principal do agente — define quem ele é (primeira posição do prompt de sistema) | ~/.hermes/SOUL.md ou $HERMES_HOME/SOUL.md |
.hermes.md / HERMES.md | Instruções do projeto (prioridade mais alta) | Sobe até a raiz do git |
AGENTS.md | Instruções do projeto, convenções de código | Percurso recursivo de diretórios |
CLAUDE.md | Arquivos de contexto do Claude Code (também detectados) | Só o diretório de trabalho |
.cursorrules | Regras do Cursor (também detectadas) | Só o diretório de trabalho |
.cursor/rules/*.mdc | Arquivos de regra do Cursor (também detectados) | Só o diretório de trabalho |
- O SOUL.md é a identidade principal do agente. Ele ocupa a primeira posição do prompt de sistema, substituindo por completo a identidade padrão embutida. Edite para personalizar quem o agente é.
- Se o SOUL.md estiver ausente, vazio ou não puder ser carregado, o Hermes cai numa identidade padrão embutida.
- Arquivos de contexto de projeto seguem um sistema de prioridade — só UM tipo é carregado, e a
primeira correspondência vence:
.hermes.md→AGENTS.md→CLAUDE.md→.cursorrules. O SOUL.md é sempre carregado à parte. - O AGENTS.md é hierárquico: se os subdiretórios também tiverem um, todos são combinados.
- O Hermes cria um
SOUL.mdpadrão sozinho, se ainda não existir. - Todos os arquivos de contexto carregados são limitados a
context_file_max_charscaracteres (padrão de 20.000), com corte inteligente.
Veja também:
Diretório de trabalho
| Contexto | Padrão |
|---|---|
CLI (hermes) | O diretório atual, de onde você rodou o comando |
| Gateway de mensagens | O terminal.cwd do ~/.hermes/config.yaml; sem valor, o diretório do usuário (~) |
| Docker / Singularity / Modal / SSH | O diretório do usuário dentro do container ou da máquina remota |
Para trocar o diretório de trabalho:
# em ~/.hermes/config.yaml:
terminal:
cwd: /home/meuusuario/projetos
O MESSAGING_CWD e as entradas diretas de TERMINAL_CWD no ~/.hermes/.env são reservas de
compatibilidade, do formato antigo. Configurações novas devem usar o terminal.cwd.
Rede
Contornos de conectividade para o tráfego HTTP de saída:
network:
force_ipv4: false # força IPv4 nas conexões de saída (padrão: false)
force_ipv4 — em servidores com IPv6 quebrado ou inalcançável, o Python resolve primeiro os
registros AAAA e pode travar pelo tempo limite inteiro do TCP antes de recuar para IPv4. Coloque
true para pular o IPv6 e conectar direto por IPv4.
Abertura
Dicas de primeira vez e a oferta estruturada de montar o perfil:
onboarding:
profile_build: "ask" # "ask" (padrão) | "off"
seen: {} # trava interna — deixe vazio
profile_build— controla a oferta de montar o perfil, feita na primeiríssima mensagem de gateway. O"ask"(padrão) oferece construir um perfil do usuário; a oferta é opcional e depende de consentimento — o agente pergunta antes de qualquer consulta e nunca lê contas conectadas em silêncio. O"off"mostra só uma apresentação simples. A oferta acontece no máximo uma vez.seen— estado interno. O Hermes trava aqui cada dica já mostrada, para ela não aparecer de novo; a oferta de perfil também é registrada aqui depois de exibida. Não edite à mão — apague a seçãoonboardinginteira se quiser rever todas as dicas.
Painel
Configuração do painel web — tema visual, URL pública e provedores de
autenticação. Os provedores (OAuth, senha simples, drenagem) estão documentados em detalhe na
página do painel; aqui está o formato no config.yaml.
dashboard:
theme: "default" # "default" | "midnight" | "ember" | "mono" | "cyberpunk" | "rose"
show_token_analytics: false # reativa as telas de análise de token e custo (só estimativa local)
public_url: "" # autoridade pública completa, para o redirect_uri do OAuth (env: HERMES_DASHBOARD_PUBLIC_URL)
trusted_proxies: [] # IPs/CIDRs de proxy autorizados a mandar cabeçalhos X-Forwarded-*
oauth: # portão OAuth do Portal (ativo com --host e sem --insecure)
client_id: "" # agent:{instance_id} — o Portal provisiona
portal_url: "" # vazio → padrão do plugin (Portal de produção)
basic_auth: # portão de usuário e senha, auto-hospedado (plugin dashboard_auth/basic)
username: "" # vazio → o plugin não faz nada
password_hash: "" # scrypt$... (preferido — sem texto puro em repouso)
password: "" # reserva em texto puro (é convertida em hash na memória, ao carregar)
secret: "" # chave de assinatura do token; vazio → aleatória por processo
session_ttl_seconds: 0 # 0 → padrão do plugin (12h)
drain_auth: # portão de credencial de serviço, para controle de drenagem
scope: "drain" # rótulo de capacidade no principal verificado
min_secret_chars: 43 # barra de entropia (caracteres url-safe-b64; 43 ≈ 256 bits)
ws_ping_interval: 20.0 # intervalo do ping de manutenção do WebSocket fora do loopback (segundos)
ws_ping_timeout: 20.0 # tempo limite do pong de manutenção (segundos)
ws_orphan_reap_grace_s: 20.0 # tolerância antes de recolher uma sessão desconectada do WS (segundos)
startup_orphan_sweep: true # fecha, no boot, as sessões órfãs de um gateway morto
theme— o tema visual do painel.show_token_analytics— desligado por padrão. A página de análise e os números de token e custo são uma estimativa local, por baixo (ela ignora chamadas auxiliares, novas tentativas, reservas e escritas de cache), então podem ficar bem abaixo da fatura do provedor. Ligue só sabendo que não é cobrança.public_url— quando definido, é a autoridade completa (esquema, host e prefixo de caminho opcional) usada para montar oredirect_urido OAuth. Defina em instalações atrás de proxy reverso que não repassam os cabeçalhosX-Forwarded-*de forma confiável. Deixe vazio para usar a reconstrução por cabeçalho de proxy.trusted_proxies— endereços IP ou redes CIDR limitadas autorizados a fornecer oX-Forwarded-Protoe oX-Forwarded-For. O loopback continua confiável automaticamente. Configure quando o proxy reverso de TLS conectar de outro container ou host. Prefira o IP exato do proxy; use uma rede pequena e dedicada só quando o endereço for dinâmico. Curingas e redes/0são recusados.oauth,basic_authedrain_auth— configuração dos provedores de autenticação, lida pelos plugins de autenticação do painel. O segredo de drenagem em si não fica aqui; ele é fornecido pela variávelHERMES_DASHBOARD_DRAIN_SECRET. Veja o painel web para a configuração completa de autenticação.ws_ping_intervalews_ping_timeout— ajuste da manutenção de conexão WebSocket fora do loopback (conexões de loopback nunca fazem ping). Aumente em ligações de latência alta (Tailscale, túneis SSH distantes), em que os 20 segundos padrão podem fabricar desconexões 1006 falsas.ws_orphan_reap_grace_s— quanto tempo uma sessão desconectada do WebSocket espera antes de ser recolhida. Aumente junto com os valores de manutenção, se os clientes reconectam devagar. (OHERMES_TUI_WS_ORPHAN_REAP_GRACE_Scontinua existindo como sobrescrita interna.)startup_orphan_sweep(padrãotrue) — o temporizador de recolhimento acima vive no processo, então um reinício do gateway (atualização, queda, systemd) antes de ele disparar deixa a linha da sessão aberta para sempre — trabalho "ativo" fantasma no/resumee nos painéis. Em todo boot do gateway — tanto na TUI por stdio (entry.main) quanto no processo auxiliar de WebSocket do desktop e do painel (handle_ws) — as linhas de origemtui,desktopousubagentcujo início e cuja mensagem mais recente sejam ambos mais velhos que o tempo de vida da sessão (HERMES_TUI_SESSION_TTL_S, padrão de 6 horas) são fechadas comend_reason: startup_orphan_reap. Sessões de plataforma de mensagem (Telegram, Discord e afins) nunca são tocadas, sessões vivas na memória (um cliente que já retomou) ficam de fora, e as sessões varridas continuam retomáveis.