Workflow SDK 5: jobs longos de IA precisam de um contrato de cancelamento

Tech

O problema de um job de IA longo não é só esperar. É saber o que ainda continuou depois do cancelamento. O modelo pode seguir rodando, um upload pode terminar após o timeout, e um retry pode gastar mais após a cota.

Em 16 de junho de 2026, a Vercel anunciou suporte a AbortController e AbortSignal entre workflows e steps no Workflow SDK 5 beta. Com sandboxes mais longas, agentes precisam parar com precisão.

Diagrama de Workflow SDK com sinais AbortController fluindo para etapas e cancelamento por timeout, usuário ou cota
A mudança principal é o contrato operacional: qual etapa recebe qual sinal, como para e qual limpeza fica visível.

O que mudou

Um workflow cria AbortController, passa signal para steps e chama abort() em timeout, corrida, hook ou regra de cota. O sinal é descrito como durável entre suspensões, replay determinístico e invocações separadas.

A cancelamento é cooperativo: o step precisa usar signal em fetch/API, chamar throwIfAborted ou checar aborted.

Por que importa

OCR, relatórios, automação de navegador, agentes multi-modelo e testes longos ganham com execução longa. Sem contrato de cancelamento surgem custos, chamadas sobrando e estados ambíguos.

Perguntas da comunidade sobre produção e cleanup confirmam a demanda operacional.

Impacto

Timeout vira política de produto; trabalho paralelo cancela perdedores; abort errors sem retry separam cancelamento de falha.

Checklist

Defina user, timeout, admin, cota, parent request.

Passe signal para cada etapa cara.

Separe run.cancel() de AbortSignal.

Registre cancelled_by_user, timed_out, quota_exceeded.

Teste cleanup externo.

Riscos

SDK 5 é beta/pre-release; comece pequeno e com rollback.

AbortSignal não desfaz side effects remotos.

Agora

Mapeie fronteiras de cancelamento antes do botão: custo, mutação externa e operações não idempotentes.

Checklist de 30 minutos

Motivos de cancelamento nomeados

Etapas caras recebem ou verificam signal

Abort e retry não entram em conflito

Efeitos externos têm idempotency keys

Observabilidade separa cancelled de failed

const controller = new AbortController();
const result = await Promise.race([
  expensiveStep(controller.signal),
  sleep("30s").then(() => null),
]);
if (result === null) controller.abort();

Fontes e leituras