O Hermes agora pede aprovação antes de desinstalar um pacote

Um pacote npm global está deixando seus builds mais lentos sem motivo aparente. Você o encontra e digita npm uninstall -g some-package sem pensar duas vezes. Dois dias depois, ele falta na CI, e todos os projetos daquela máquina são arrastados junto. Instalar algo é fácil de notar; remover algo é fácil de esquecer — e o estrago quase sempre é compartilhado. E, mesmo assim, quando o Hermes Agent executava esse comando por você, nada parava para perguntar: o portão de aprovação de comandos perigosos só conhecia primitivas de shell como rm -rf. O PR #109170 fecha exatamente essa lacuna.
Instalar raramente dói. Remover dói.
Primeiro, o mecanismo. A aprovação de comandos perigosos é um conjunto de regras de padrão embutidas no Hermes: quando o agente está prestes a executar um comando cujas consequências não podem ser desfeitas, ele mostra um pedido de aprovação e espera por você em vez de simplesmente executar. A intenção é simples: que uma operação irreversível não passe despercebida.
Visto por esse ângulo, instalar e desinstalar não são simétricos. Instalar é aditivo: o que aparece você acaba notando, e se der problema é só apagar. Desinstalar é subtrativo, e normalmente uma subtração compartilhada: remova um pacote global e todos os projetos da máquina que dependiam dele são afetados, sem que nada avise que alguma coisa acabou de desaparecer.
É justamente essa assimetria que explica por que a mudança barra só as remoções. npm update -g, pip install, brew upgrade e yarn install continuam sem ser marcados e continuam sem perguntar nada. Se a instalação também fosse barrada, a barra de aprovação viraria um ruído que você aceita uma dúzia de vezes por dia — pior do que não ter barreira nenhuma.
A tabela de padrões só conhecia primitivas de shell
O problema estava em DANGEROUS_PATTERNS, a tabela de padrões de tools/approval_detection.py. O que ela pegava eram primitivas de shell: rm -rf, git reset --hard e companhia — comandos brutos, muito conhecidos, óbvios à primeira vista. Para os verbos de remoção dos gerenciadores de pacotes não havia regra alguma.
Por isso, quando o incidente por trás da issue #10199 aconteceu de fato, o comando rodou do começo ao fim sem nenhuma pergunta. Não foi um veredicto errado: é que nenhuma regra cobria aquele comando.
As cinco formas de remover que agora são barradas
O PR #109170 vai pelo caminho direto: adiciona em DANGEROUS_PATTERNS uma regra "package manager uninstall" por gerenciador de pacotes. O diff completo é +34 / −0 em dois arquivos, tools/approval_detection.py e tests/tools/test_approval.py.
De agora em diante, tudo isto passa pela aprovação de comandos perigosos:
- npm —
npm uninstall -g some-package. Verbos de remoção:uninstall/unlink/remove/rm/r/un - pnpm —
pnpm remove. Verbos de remoção:uninstall/remove/rm/un - yarn —
yarn remove. A regra aceita umglobalopcional na frente e depoisuninstall/remove - pip / pip3 —
pip uninstall. O verbo de remoção éuninstall - brew —
brew uninstall. Verbos de remoção:uninstall/remove/rm
Repare no que as regras casam: o verbo de remoção do gerenciador, não um nome de pacote específico. Não importa em que o verbo aponta nem quantos alvos vêm depois — o comando é marcado.
Dois pontos em que evitaram falsos positivos
A forma clássica de quebrar uma tabela de regras é deixá-la ampla demais e começar a barrar comandos legítimos. Dois detalhes aqui merecem destaque.
Primeiro, as regras são ancoradas na posição do comando com _CMDPOS. Como as outras regras de nome de comando nesse arquivo, o padrão só casa onde o comando realmente começa. Por isso o texto entre aspas não é mal interpretado: git commit -m "document npm uninstall usage" é só uma mensagem de commit e não dispara nada.
Segundo, cada opção global aceita um argumento que não seja um hífen. Ou seja, npm --prefix DIR uninstall x, pip --proxy URL uninstall x e yarn --cwd DIR remove x também são pegos. Uma opção seguida de um argumento é uma forma do dia a dia; se ela escapar, a regra vira decoração.
Os testes vieram junto. tests/tools/test_approval.py adiciona 6 casos parametrizados que agrupam seis formas de remoção sob uma mesma chave. Esses casos ficavam vermelhos contra o código antigo; depois da correção, 129 casos passam, e a suíte inteira de tests/tools/ fica verde com 9141.
O que isso significa para você
Se tem alguém acompanhando sua sessão do Hermes, a coisa é simples: o agente para e pergunta antes de desinstalar um pacote, e você confirma ou recusa. Um clique a mais em troca de saber que o que foi apagado é o que você queria apagar.
O caso que realmente merece reflexão antecipada é o trabalho sem supervisão: tarefas de cron, agentes rodando em segundo plano. Não há ninguém para responder a um pedido de aprovação, então uma desinstalação que deveria ter sido barrada fica parada no meio do caminho. Se um fluxo automatizado precisa mesmo desinstalar pacotes, não conte com a sorte: teste antes o comando contra os guardiões reais com hermes approvals test, veja em qual veredicto ele cai e só então decida se permite, se reformula o fluxo ou se simplesmente mantém o agente longe dessa etapa. O comportamento das aprovações em contexto de cron está detalhado no guia de aprovações sem supervisão.
Uma distinção que vale manter clara: a aprovação de comandos perigosos e approvals.deny são dois mecanismos diferentes. A primeira é a tabela de padrões embutida e funciona sem nenhuma configuração sua; a segunda é uma lista de negação predefinida que você mesmo mantém. Este artigo trata apenas da primeira. Para a segunda, veja a configuração de aprovações inteligentes — não é preciso repetir aqui.
E, por fim, o status de publicação, sem rodeios: isso entrou na main em 2026-09-12, depois da v0.21.2 (lançada em 2026-09-11). Portanto ainda não está em nenhuma versão lançada — está na linha principal e chega até você na próxima versão. Para saber o que o seu build atual faria, o hermes approvals test é a resposta direta; as notas de lançamento da v0.21.2 são o lançamento mais recente antes dessa mudança.
Em resumo
- As desinstalações de gerenciadores de pacotes agora passam pela aprovação de comandos perigosos:
npm uninstall -g,pnpm remove,yarn remove,pip uninstallebrew uninstallestão cobertas. - Instalações e atualizações continuam sem perguntar:
npm update -g,pip install,brew upgradeeyarn installnão são afetadas. A aprovação fica reservada ao que é realmente irreversível. - As regras são ancoradas na posição do comando, então um
npm uninstallentre aspas não é marcado por engano, e uma opção global seguida de um argumento não as contorna. - A mudança está mesclada na
maine ainda não saiu em nenhuma versão.