O ciclo de uma execução
Uma execução tem três camadas encaixadas. Entender a ordem resolve a maior parte das dúvidas de "por que isso aconteceu antes daquilo".
A visão de cima
app.run()
└─ workflow
└─ passo 1, passo 2, passo 3… ← a ordem que você declarou em `steps`
└─ cada passo é um agente, um `parallel` ou um `loop`
└─ turno do agente
├─ monta as mensagens
├─ chama o modelo
├─ se pediu ferramenta: valida, executa, guarda o resultado
└─ devolve a respostaO loop repete o bloco interno até o until ser verdadeiro. O parallel roda os passos ao mesmo tempo, sobre o mesmo estado.
Um turno de agente, em detalhe
É aqui que os hooks entram. A ordem exata:
beforePrompt(prompt, ctx) ← altera o system prompt
↓
chamada ao modelo
↓
pediu ferramenta?
├─ sim → beforeTool(call, ctx) ← troca args, ou `throw` cancela
│ ↓
│ execute(args) ← seu código, com args já validados
│ ↓
│ afterTool(result, ctx) ← transforma o resultado
└─ não → (segue)
↓
afterResponse(response, ctx) ← transforma a resposta do turno
↓
grava ctx.turn e ctx.output
qualquer throw acima → onError(error, ctx)Todos os hooks são opcionais. Sem nenhum, o turno acontece igual — eles só existem para quando você precisa entrar no meio.
Contrato dos hooks
Retornar um valor substitui. Retornar undefined mantém o original. É por isso que um beforePrompt que só quer observar pode simplesmente não retornar nada.
Um turno é uma chamada, não a tarefa toda
Distinção que confunde no começo: um turno do agente é uma volta só — uma chamada ao modelo e, no máximo, uma ferramenta.
Se a tarefa exige investigar antes de responder, você precisa de várias voltas. É para isso que serve o loop:
loop({
steps: [LeitorAgent],
until: untilAnswered,
maxIterations: 8,
})Sem o loop, o agente chamaria uma ferramenta e o workflow terminaria ali — com o resultado da ferramenta como saída, sem o modelo ter tido chance de interpretá-lo.
O que sobrevive entre os passos
Todos os passos de um workflow compartilham o mesmo estado. Um agente adiciona seu turno ao histórico; o próximo agente já enxerga a conversa até ali.
Isso é útil e tem uma consequência que vale saber desde já: a resposta de um passo entra no próximo como fala do assistente. Se você quer que ela seja contexto em vez de fala, Estado e contexto mostra como.
Onde ver isso acontecendo
Ligue o log no config e a árvore aparece ao vivo:
export const config: ThenaConfig = { log: true };[thena] ▸ workflow LeitorWorkflow
[thena] ▸ loop
[thena] ▸ agent LeitorAgent
[thena] ▸ chat
[thena] ▸ tool ler_arquivo
[thena] ◂ tool ler_arquivo 12ms ✓
[thena] ◂ chat 1.84s ✓
[thena] ◂ agent LeitorAgent 1.85s ✓Com report: true você ganha o mesmo em HTML, com o conteúdo de cada passo — veja Report.
Próximo
Estado e contexto — o que é ctx, o que é state, e como mexer nos dois.
