Start Debugging

PreModelSwitch: o Claude Code agora pode vetar uma troca de modelo

O Claude Code 2.1.251 adiciona os eventos de hook PreModelSwitch e PostModelSwitch. O matcher dispara com o nome canônico do modelo para o qual você troca, e o código de saída 2 cancela a troca.

Todos os eventos de hook que o Claude Code havia lançado antes desta semana vigiavam algo que o modelo faz: PreToolUse vê um comando Bash antes de ele rodar, PermissionRequest vê a solicitação antes de você respondê-la, PreCompact vê a transcrição antes de ela ser resumida. A versão 2.1.251, lançada em 2026-08-28, adicionou o primeiro par que vigia o próprio modelo. PreModelSwitch e PostModelSwitch disparam quando a sessão muda quais pesos estão respondendo.

Por que uma troca de modelo merece um portão

O modelo de uma sessão não é uma preferência, é uma entrada. Troque Opus por Haiku no meio de uma refatoração e a próxima chamada de ferramenta será planejada por outro raciocinador sobre a mesma transcrição. As equipes se importam com isso por três motivos distintos: custo (uma troca de /model para cima pode multiplicar a conta dos turnos restantes), reprodutibilidade (um relato de bug que diz “o Claude fez X” é infalsificável se o modelo mudou no meio da sessão) e política (algumas organizações só têm autorização para enviar código a modelos específicos).

Até a 2.1.251 não havia costura alguma para aplicar nada disso. Agora há.

Bloqueando uma troca

Registre o hook em settings.json. Aqui o matcher não é um nome de ferramenta: ele casa com o nome canônico do modelo para o qual a sessão está trocando:

{
  "hooks": {
    "PreModelSwitch": [
      {
        "matcher": "claude-opus-5",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/check-model-switch.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Matchers são expressões regulares, então tanto claude-opus-4-6|claude-opus-5 quanto .*opus.* funcionam se você quiser pegar uma família inteira em vez de um único ID.

O hook lê o evento pelo stdin. PreModelSwitch e PostModelSwitch recebem from_model e to_model no lugar dos campos usuais de ferramenta, junto com session_id, prompt_id, transcript_path e cwd:

#!/usr/bin/env bash
to_model=$(jq -r '.to_model')

if [ -n "$OPUS_BUDGET_EXHAUSTED" ]; then
  cat <<JSON
{
  "hookSpecificOutput": {
    "hookEventName": "PreModelSwitch",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Opus budget for this repo is spent. Staying on $to_model is blocked until the cycle resets."
  }
}
JSON
fi
exit 0

Sair com código 2 também bloqueia a troca, que é a versão de uma linha caso você não queira emitir JSON. Uma aresta afiada que vale conhecer: um hook PreModelSwitch cancelado no seu timeout também bloqueia a troca. Esse evento falha em modo fechado, ao contrário da maior parte do ciclo de vida.

PostModelSwitch dispara quando você não pediu

PostModelSwitch é a metade de auditoria, e cobre mais do que as suas próprias chamadas de /model. Segundo a documentação, ele roda “after the session’s model changes, including changes Claude Code makes on its own, such as restoring the model when you resume a session”. Esse é exatamente o caso que torna difícil responder depois “qual modelo escreveu isto”, então acrescentar from_model, to_model e session_id a um arquivo de log aqui é a observabilidade mais barata que você vai adicionar na semana inteira.

A mesma versão também corrigiu as requisições do Opus 5 que falhavam com “effort is not supported when thinking is disabled” em effort xhigh ou max, e fechou quatro formas distintas de contornar a verificação de permissão. Todos os detalhes estão na referência de hooks.

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Voltar