# Tokens de API expirados: regras de recuperação para tarefas longas de agentes

Tarefas longas de agentes falham de um jeito particularmente absurdo quando ninguém é responsável pela expiração do token. O agente vê um 401, repete a mesma chamada, consome a cota de limite de requisições e, às vezes, transforma uma escrita incerta em várias. Isso não é uma falha de autenticação. É uma falha no projeto da recuperação.

Planeje a expiração como uma mudança de estado esperada. Entregue a renovação a um único componente, classifique as falhas antes de agir, vincule as tentativas à semântica da operação e deixe um registro que permita ao operador ver se o lado remoto já aceitou o trabalho. Um agente nunca deve precisar adivinhar se pode criar credenciais ou se é seguro enviar uma escrita novamente.

## A expiração é uma mudança de estado, não uma indisponibilidade excepcional

Um token de API expirado informa que a autorização de uma credencial de curta duração terminou. Ele não informa que a tarefa falhou. Uma tarefa longa pode ter concluído dez operações remotas antes que a próxima solicitação encontre a expiração. O código de recuperação precisa preservar essa diferença.

As equipes costumam juntar quatro eventos diferentes em um único ramo chamado `auth_failed`. Esse atalho cria comportamentos ruins porque cada evento exige uma resposta diferente:

- Expiração significa que a validade do token terminou e que o responsável autorizado pela renovação pode solicitar outro token de acesso.
- Revogação significa que uma pessoa usuária, um administrador ou o provedor retirou a concessão. A renovação pode falhar por definição.
- Uma autenticação inválida na solicitação pode significar um cabeçalho malformado, um tipo de credencial incorreto, uma incompatibilidade de emissor ou um audience incorreto.
- Permissão insuficiente significa que a identidade continua válida, mas não pode executar essa operação.

O OAuth diferencia esses casos por um motivo. A RFC 6750 especifica o erro de token bearer `invalid_token` e diz que um servidor de recursos usa uma resposta 401 com um desafio `WWW-Authenticate` quando o token está expirado, revogado, malformado ou inválido por outro motivo. Isso é uma orientação útil do protocolo, mas não dá ao cliente permissão para renovar às cegas. O servidor de recursos apenas informa que rejeitou esta solicitação.

Uma tarefa também tem duas linhas do tempo. A **linha do tempo do trabalho** registra o que ela descobriu, calculou, criou e confirmou. A **linha do tempo da autorização** registra a geração da credencial que permitiu sua atuação. Quando um token expirar, preserve a linha do tempo do trabalho e mude a linha do tempo da autorização para `renewing` ou `blocked`. Não reinicie a tarefa inteira e chame isso de recuperação.

Essa diferença é mais importante para agentes porque eles fazem uma cadeia de chamadas dependentes. Suponha que um agente crie uma solicitação de mudança, envie um artefato e perca o acesso antes de conseguir anexar o artefato. Reiniciar a partir da primeira instrução pode criar uma segunda solicitação de mudança. Um checkpoint persistente depois de cada efeito remoto confirmado permite que o agente continue pelo anexo ausente, em vez de repetir todo o plano.

Use estados explícitos em vez de um booleano como `authenticated`:

```text
ready -> executing -> authorization_expired -> renewal_in_progress
renewal_in_progress -> executing
renewal_in_progress -> authorization_blocked
executing -> outcome_unknown
outcome_unknown -> reconciled -> executing
```

`outcome_unknown` merece um estado próprio. Uma conexão pode cair depois que o serviço confirma uma escrita, mas antes que o chamador receba a resposta. A renovação do token não resolve essa incerteza. A tarefa precisa consultar o serviço usando seu identificador de operação, token de idempotência ou uma busca específica do provedor antes de tentar a escrita novamente.

## Um único componente deve ser responsável pela renovação

O cliente que mantém a credencial de renovação deve cuidar da renovação do token de acesso, e o agente deve solicitar uma ação em vez de receber credenciais renováveis. Essa regra parece restritiva até que dois agentes encontrem a expiração ao mesmo tempo.

Se cada worker carregar uma cópia de um token de renovação, cada worker poderá renovar de forma independente. Eles entram em disputa, produzem um histórico cheio de eventos de credenciais sem relação entre si e, quando há rotação do token de renovação, podem invalidar um token que outro worker ainda mantém. Mais importante, você transformou todo processo que pode ler uma tarefa em um portador de identidade de longa duração.

Coloque um broker de credenciais ou gateway de ações entre os agentes e o provedor. O broker armazena a credencial de renovação ou de serviço, obtém tokens de acesso de curta duração, anexa um token apenas quando executa uma solicitação e devolve a resposta. O agente não recebe um token de acesso nem um token de renovação.

Essa divisão dá a cada participante uma função clara:

- O agente decide qual operação permitida quer executar e fornece os dados da operação.
- O gateway verifica se a sessão pode fazer a solicitação, seleciona a referência da credencial e executa a chamada.
- O responsável pela renovação renova uma vez quando o provedor informa uma falha de expiração classificada.
- Um operador cuida de uma concessão revogada, de uma nova exigência de consentimento ou de uma credencial que precisa de intervenção humana.

Não faça do agente o responsável pela renovação apenas porque ele pode chamar um endpoint OAuth. Capacidade e autoridade são coisas diferentes. Um agente que pode solicitar um evento de calendário ou implantar um artefato não precisa automaticamente de permissão para estender a identidade de uma pessoa ou de um serviço.

A RFC 6749 descreve tokens de renovação como credenciais emitidas para o cliente e usadas para obter novos tokens de acesso. Leia literalmente a palavra «cliente» na sua arquitetura. Se o seu agente não for o cliente registrado, ele não deve herdar o token de renovação do cliente apenas porque produz a solicitação da API.

Há casos legítimos em que a própria tarefa controla a renovação. Uma carga de trabalho de máquina com escopo restrito, seu próprio cliente registrado, seu próprio limite de armazenamento e sem delegação humana pode fazer isso. Mesmo assim, um único coordenador de renovação deve atender todas as operações concorrentes dessa identidade. Use um mutex ou um mecanismo single-flight identificado pela referência da credencial. A primeira chamada que falhar renova; as outras aguardam o resultado em vez de sobrecarregar o endpoint de tokens.

Um registro simples de responsabilidade evita projetos vagos:

```json
{
  "credential_ref": "billing-write-prod",
  "renewal_owner": "action-gateway",
  "access_token_lifetime": "provider-defined",
  "refresh_allowed": true,
  "reauthorization_owner": "on-call-operator",
  "concurrent_refresh": "single-flight"
}
```

O registro contém uma referência, nunca a credencial em si. Ele também informa quem deve agir quando a renovação deixar de funcionar. Se ninguém puder responder a essa pergunta antes da implantação, a tarefa responderá mal durante a noite.

## Um 401 precisa de evidências antes de acionar a renovação

Renove apenas quando a resposta e o registro da credencial sustentarem o diagnóstico de expiração. Tratar todo 401 como expiração esconde falhas de configuração e pode gerar uma longa sequência de tentativas de renovação inúteis.

Comece pelo corpo de erro, pelos cabeçalhos e pelas expectativas de formato de token documentados pelo provedor. Algumas APIs retornam valores `WWW-Authenticate` compatíveis com OAuth. Outras retornam códigos de erro em JSON. Algumas colocam as falhas de autenticação atrás de um gateway que usa outro código de status. Seu classificador deve usar os sinais documentados para esse provedor e, depois, recorrer a um erro terminal seguro.

Este é um contrato de classificação possível:

```json
{
  "http_status": 401,
  "provider_code": "invalid_token",
  "www_authenticate": "Bearer error=\"invalid_token\"",
  "credential_ref": "reports-read",
  "token_generation": 17,
  "decision": "renew_once"
}
```

Uma resposta só pode receber `renew_once` se todas estas condições forem atendidas: a solicitação usou uma credencial emitida ou selecionada pelo seu gateway, essa credencial tem um caminho de renovação, o sinal do provedor corresponde à condição documentada de expiração ou token inválido e esta tarefa ainda não renovou a geração 17.

Use um resultado diferente para cada classe de falha. Um cabeçalho malformado pertence a `configuration_error`, para que uma pessoa desenvolvedora possa inspecionar o construtor da solicitação. Uma incompatibilidade de audience pertence a `credential_binding_error`, em que alguém precisa corrigir a solicitação do token ou a configuração do recurso. Uma concessão revogada pertence a `reauthorization_required`, em que o sistema interrompe ações externas e informa ao operador correto exatamente qual identidade precisa de consentimento. Um 403 pertence a `permission_denied`; renová-lo é agir por hábito, sem fundamento.

Problemas no relógio causam uma parcela surpreendente dos diagnósticos incorretos. Um cliente que calcula a expiração local pode rejeitar cedo demais um token utilizável, enquanto um cliente com relógio desajustado pode enviar um token expirado. Registre a expiração fornecida pelo emissor quando receber o token, mantenha uma pequena margem de segurança e use um relógio de sistema confiável. Não deixe cada agente calcular sua própria expiração a partir de uma claim decodificada. Isso duplica a lógica do protocolo e convida a divergências.

Não inspecione o conteúdo de um token apenas para decidir se pode confiar nele. Um JSON Web Token pode conter uma claim `exp`, mas decodificar seu conteúdo base64url não verifica sua assinatura, seu emissor, seu audience nem seu estado de revogação. Use esse dado apenas como indicação, depois que o componente que recebeu o token tiver concluído a validação documentada pelo provedor. Tokens de acesso opacos não oferecem conteúdo para inspeção, o que é mais um motivo para fazer o chamador depender do tratamento da resposta, e não de uma arqueologia do token.

## Limites de tentativas protegem o sistema remoto e suas evidências

Depois de uma expiração classificada, permita uma renovação coordenada e uma nova tentativa controlada. Mais tentativas não melhoram a autorização; na maioria das vezes, apenas escondem um caminho de renovação quebrado e dificultam a leitura do registro de auditoria.

A regra de repetição depende do que a operação pode fazer. Uma solicitação de leitura normalmente tolera uma nova tentativa depois da renovação. Uma solicitação de escrita precisa de evidências mais fortes porque o serviço remoto pode tê-la processado antes que a resposta de expiração, o timeout ou a perda de conexão chegasse ao chamador.

Classifique as operações ao projetar a interface de ações:

| Classe da operação | Exemplo | Recuperação depois da renovação |
| --- | --- | --- |
| Leitura | Buscar um registro | Repetir uma vez se a solicitação não tiver efeito colateral externo |
| Escrita idempotente | Substituir um documento em uma versão conhecida | Repetir uma vez se o provedor garantir idempotência para esse método e condição |
| Escrita com token de idempotência | Criar um rascunho de fatura | Reutilizar exatamente o mesmo token e payload uma vez |
| Escrita não idempotente | Enviar uma mensagem ou iniciar um pagamento | Reconciliar primeiro; só agir se o sistema remoto confirmar que não houve efeito anterior |

Os nomes dos métodos HTTP não resolvem essa questão. `PUT` costuma ter intenção idempotente, mas um provedor pode associar a ele uma notificação por e-mail ou uma ação assíncrona posterior. `POST` pode ser seguro se o provedor aceitar um campo de idempotência. Leia o contrato do endpoint e teste o comportamento real.

Para operações com token de idempotência, crie o token antes da primeira chamada de rede e persista-o com uma impressão digital canônica da solicitação. Em toda nova tentativa, envie exatamente o mesmo token e um payload logicamente idêntico. Não gere um token novo depois de um 401. Um token novo informa ao provedor que se trata de uma operação diferente, anulando o objetivo do mecanismo.

```json
{
  "operation_id": "job-84f3/create-draft",
  "idempotency_token": "a stable random value stored before send",
  "request_fingerprint": "method, path, normalized body hash",
  "attempt": 1,
  "authorization_generation": 17
}
```

A expressão «normalized body hash» é importante. Se o construtor da nova tentativa alterar um timestamp, a ordem de um array ou um rótulo gerado, poderá transformar silenciosamente o mesmo token de idempotência em uma solicitação incompatível. Alguns provedores rejeitam essa divergência. Outros lidam com ela de forma inconsistente. Mantenha o primeiro corpo de solicitação serializado ou faça a canonização uma única vez e reutilize o resultado.

A lógica de tentativas por limite de requisições e por rede deve compartilhar esse orçamento. Um agente que usa três tentativas de rede, depois uma tentativa de renovação e depois mais três tentativas de rede criou sete oportunidades de duplicar ou sobrecarregar uma operação. Defina um orçamento de tentativas no nível da operação. Por exemplo, uma leitura segura pode permitir uma chamada inicial, um caminho de renovação e uma nova tentativa. Uma ação semelhante a um pagamento pode permitir uma chamada inicial e, depois, apenas reconciliação.

Registre também cada tentativa suprimida. Os operadores precisam ver que o sistema parou intencionalmente depois de `renewal_attempted=true`, em vez de presumir que o agente caiu. Esse registro também fornece um sinal claro quando um provedor altera o formato de erro e o classificador passa a recusar uma recuperação que antes permitia.

## Checkpoints permitem retomar uma tarefa sem inventar o passado

Uma tarefa longa de agente deve persistir separadamente os efeitos remotos concluídos e a intenção pendente, porque um token renovado não informa o que aconteceu antes da falha. O padrão ruim mais comum armazena apenas uma transcrição da conversa ou o plano final do agente e depois pede que ele reconstrua o estado após uma interrupção.

Use um diário de tarefas com registros que um programa possa reconciliar. Cada chamada externa pretendida precisa de um ID de operação estável. Cada resultado confirmado precisa do ID do recurso fornecido pelo provedor, da versão ou ETag, se disponível, e da impressão digital da solicitação. Cada resultado incerto precisa da regra de consulta que o resolve.

Um checkpoint compacto pode ter esta aparência:

```json
{
  "task_id": "release-2025-04-17-42",
  "completed": [
    {"operation_id": "create-change", "remote_id": "CR-819", "version": "6"},
    {"operation_id": "upload-bundle", "remote_id": "asset-552"}
  ],
  "pending": {
    "operation_id": "attach-bundle",
    "request_fingerprint": "POST /changes/CR-819/assets body-sha256:...",
    "reconcile": "list assets for CR-819 and match asset-552"
  },
  "authorization_state": "authorization_expired"
}
```

A tarefa não precisa salvar cada pensamento intermediário. Ela precisa de fatos suficientes para determinar a próxima ação remota segura. Mantenha segredos, cabeçalhos bearer e respostas de renovação fora deste diário. Esses materiais pertencem ao responsável pelas credenciais, não ao armazenamento geral das tarefas.

As condições de versão importam durante uma pausa. Se a tarefa leu a versão 6 de um documento antes da expiração e retomar uma hora depois, outro participante pode tê-lo alterado. Use ETags, números de revisão, cabeçalhos condicionais ou campos de concorrência específicos do provedor quando disponíveis. Se a condição falhar, informe ao agente que o plano antigo já não se aplica. Não renove o token e sobrescreva um estado mais novo porque a tarefa acredita ser dona do mundo.

É aqui que o trabalho autônomo precisa de um limite para o julgamento. Uma tarefa pode retomar com segurança um upload cujo destino e hash já registrou. Ela não deve reprojetar casualmente uma implantação, modificar um registro de aprovação ou escolher outro destino depois que o contexto original envelheceu. Marque essas operações como dependentes de uma nova confirmação depois da recuperação.

## A resposta de recuperação deve informar ao agente o que ele pode fazer

Um gateway de ações deve devolver um resultado de recuperação estruturado, não uma frase vaga sobre autenticação que convide o agente a improvisar. O resultado deve informar se a chamada foi executada, se o gateway renovou a autorização, se uma nova tentativa é permitida e se uma pessoa precisa intervir.

Um resultado útil separa o estado da execução do estado da credencial:

```json
{
  "operation_id": "attach-bundle",
  "execution_state": "not_sent",
  "authorization_state": "reauthorization_required",
  "retry_allowed": false,
  "credential_ref": "release-api",
  "operator_action": "Reauthorize the release-api connection, then resume task release-2025-04-17-42",
  "safe_resume_from": "attach-bundle"
}
```

`not_sent` significa que o gateway parou antes de entregar a solicitação ao cliente de rede. `outcome_unknown` significa que ele não pode fazer essa afirmação. Não deixe que os dois casos sejam reduzidos a `failed`. O primeiro pode aguardar a reautorização. O segundo precisa ser reconciliado com o serviço remoto antes de qualquer nova tentativa.

Os agentes também precisam de um vocabulário restrito para a recuperação. Dê a eles resultados como `completed`, `renewed_and_replayed`, `needs_reconciliation`, `reauthorization_required`, `permission_denied` e `configuration_error`. Cada resultado deve corresponder a um único comportamento permitido. Por exemplo, o agente pode continuar depois de `renewed_and_replayed`; pode executar uma reconciliação somente de leitura documentada depois de `needs_reconciliation`; e deve interromper escritas externas depois de `reauthorization_required`.

Evite devolver respostas brutas do provedor como único sinal. Os detalhes brutos ajudam no diagnóstico, mas os agentes podem interpretá-los mal, especialmente quando os provedores usam textos inconsistentes. Mantenha a resposta bruta em um registro de diagnóstico protegido e devolva ao chamador uma decisão estável e legível por máquina.

Uma boa mensagem de erro informa a referência da identidade e a operação bloqueada sem expor material secreto. «A credencial `release-api` precisa de reautorização antes que `attach-bundle` possa ser executada» informa ao operador onde agir. «Não autorizado» não informa nada a ninguém.

## As credenciais de renovação precisam de controles mais rígidos que os tokens de acesso

Uma credencial de renovação merece proteção mais forte porque normalmente pode durar mais que o token de acesso que substitui. Não resolva a expiração distribuindo essa credencial de longa duração para cada workspace de agente, diretório de build, variável de ambiente ou transcrição.

A OAuth 2.0 Security Best Current Practice, RFC 9700, recomenda a rotação de tokens de renovação ou tokens de renovação vinculados ao remetente para clientes públicos. O suporte exato varia entre provedores, mas a lição de segurança continua válida mesmo quando o provedor usa outro protocolo: uma credencial renovável roubada oferece uma janela de uso indevido muito maior que um token bearer comum de curta duração.

Mantenha o material de renovação no armazenamento criptografado de credenciais do gateway de ações. Restrinja quais definições de ações podem selecioná-lo, exija aprovação humana explícita quando a ação justificar isso e faça da reautorização uma ação separada do operador. Um armazenamento de credenciais bloqueado deve negar o trabalho, não permitir que os agentes recorram a segredos copiados em arquivos de configuração.

O Sallyport segue esse modelo para ações HTTP e SSH compatíveis: seu cofre criptografado mantém a credencial, enquanto o agente solicita uma ação por meio de seu shim MCP e recebe apenas o resultado. Essa divisão é útil porque o agente não pode imprimir uma credencial de renovação em seu próprio contexto quando algo dá errado durante a recuperação.

Tenha disciplina com as respostas de renovação. Alguns provedores fazem a rotação da credencial de renovação e invalidam o valor antigo quando ele é usado. O responsável pela renovação precisa substituir atomicamente o valor armazenado antes de liberar as chamadas em espera. Se gravar o novo token de acesso, mas perder a credencial de renovação substituta, a tarefa poderá funcionar por pouco tempo e falhar permanentemente na próxima expiração.

Nunca registre estes campos: `Authorization`, token de acesso, token de renovação, segredo do cliente, asserção assinada ou corpo completo do endpoint de tokens. Mascarar não basta quando os sistemas copiam objetos de solicitação brutos antes de o mascarador ser executado. Projete o logger para aceitar referências de credenciais e números de geração do token, em vez de estruturas que contenham segredos.

## A aprovação humana deve restaurar a autoridade, não criar uma tempestade de tentativas

A aprovação humana só ajuda quando corresponde a uma decisão clara: permitir esta execução do agente, permitir esta chamada sensível ou restaurar uma autorização revogada. Um botão genérico de «tentar novamente» depois da expiração costuma transformar o operador em um carimbo para uma ação pouco clara.

Separe as aprovações. A reautorização dá ao responsável pela credencial uma nova concessão ou um caminho de renovação utilizável. A autorização da sessão decide se esse processo específico do agente pode solicitar ações. A aprovação por chamada decide se uma operação sensível pode acontecer agora. São decisões diferentes, e juntá-las produz prompts excessivos ou permissões permanentes demais.

Quando uma credencial precisar de reautorização, mostre a referência da credencial afetada, o rótulo da identidade ou conexão, a operação bloqueada e o checkpoint da tarefa. Não mostre o token. Depois que o operador restaurar a autorização, o gateway deve retomar apenas a operação pendente registrada no checkpoint. Ele não deve repetir silenciosamente todas as chamadas falhas da transcrição do agente.

A escada fixa de decisões do Sallyport se encaixa bem nesse limite: um cofre bloqueado nega todas as ações, novos processos de agentes precisam de aprovação de sessão por padrão e determinadas credenciais podem exigir aprovação a cada uso. Os controles não tentam inferir a intenção a partir de um conjunto de regras, o que ajuda quando uma credencial expirada interrompe uma execução que, de resto, era legítima.

A fadiga de aprovação costuma ser um erro de projeto. Se um operador vê uma dúzia de prompts porque dez chamadas paralelas perceberam a mesma expiração, o coordenador de renovação não conseguiu agrupar o evento. Apresente uma única solicitação de reautorização, faça as outras chamadas aguardarem e informe a decisão resultante a cada tarefa.

Não use a aprovação para encobrir um resultado de escrita desconhecido. Nesse caso, o prompt correto pergunta se o operador quer que o sistema reconcilie o estado remoto, não se quer tentar novamente. Uma pessoa pode autorizar uma duplicação por engano com a mesma facilidade que um agente.

## Os testes de expiração devem incluir escritas incertas e workers concorrentes

Um teste de renovação de token que devolve um 401 sintético antes de uma leitura prova muito pouco. As falhas que mais prejudicam as equipes aparecem nos limites: durante um trabalho paralelo, depois que uma escrita é confirmada, quando ocorre uma rotação de renovação ou quando uma concessão é revogada.

Crie um provedor de teste ou fixture HTTP controlável que registre os IDs de operação recebidos e possa inserir falhas em pontos definidos. Suas asserções devem examinar tanto o registro do efeito remoto quanto o registro de auditoria local. Uma resposta final bem-sucedida, sozinha, pode esconder uma criação duplicada.

Execute estes casos antes de confiar em um agente de longa duração:

1. Expire o token de acesso antes de uma leitura. Confirme que apenas um worker renova e que todas as leituras em espera usam a geração substituta.
2. Expire o token antes de uma escrita idempotente. Confirme que o gateway renova uma vez e repete a chamada com o ID de operação e o corpo originais.
3. Descarte a resposta depois que o provedor registrar uma escrita não idempotente. Confirme que a tarefa entra em `outcome_unknown`, executa sua consulta e não cria um segundo efeito.
4. Revogue a concessão antes da renovação. Confirme que todas as ações dependentes param com `reauthorization_required` e que nenhum loop chama repetidamente o endpoint de tokens.
5. Retorne uma credencial de renovação rotacionada e depois interrompa o armazenamento. Confirme que o gateway detecta a substituição incompleta e bloqueia renovações futuras em vez de usar uma cópia antiga.

Teste o tempo de forma explícita. Injete um relógio no componente de credenciais para poder posicionar a expiração logo antes da construção da solicitação, logo depois da construção do cabeçalho e enquanto uma solicitação aguarda na fila. Esperar até que um token real expire torna os testes lentos e deixa sem cobertura os casos importantes de sincronização.

Por fim, teste o caminho de auditoria sem acesso ao cofre. Você deve conseguir verificar que a sequência de chamadas tentadas e decisões de renovação não mudou, mesmo sem conseguir descriptografar todos os registros. O `sp audit verify` do Sallyport verifica sua cadeia de hashes sobre dados de auditoria criptografados sem precisar de uma chave do cofre, que é o formato certo de verificação para um incidente em que o acesso às credenciais pode continuar bloqueado.

Uma tarefa que lida bem com a expiração faz menos depois que a autorização falha. Ela para, classifica a falha, permite que um único responsável renove, reconcilia a incerteza e retoma apenas a operação registrada. Essa contenção evita efeitos colaterais duplicados e deixa o operador com evidências, não com uma pilha de tentativas.
