Skip to main content
Quando o dispatcher executa múltiplos agents em paralelo, o terminal exibe um painel de progresso em tempo real que mostra o estado de cada agent individualmente. Isso elimina a incerteza de “sera que ainda está rodando?” sem precisar consultar logs.

O Que Você Ve

Durante a execução paralela, o terminal renderiza um display multi-linha atualizado a cada 100ms:
Com o registry de runs do Agent Squad, os slots em execução vão além do spinner: cada linha mostra o turno ReAct atual e a ação em andamento do worker (turno 7/30 · patch cli/foo.go), e subagents criados por um worker aparecem como sub-linhas indentadas abaixo dele.

Elementos do Display

Icones de Status por Agent


Arquitetura Interna

O sistema de progresso live usa 3 goroutines independentes comunicando via estado compartilhado protegido por mutex:

Fluxo Detalhado

1

Inicializacao

O agent_mode.go cria um AgentProgressState com N slots (um por agent) e um channel progressCh com buffer N×2.
2

Goroutine Consumer

Uma goroutine dedicada consome eventos do progressCh e atualiza o estado compartilhado via métodos Mark*() protegidos por mutex.
3

Timer Display

O turnTimer inicia com um callback que, a cada 100ms:
  1. Limpa as linhas anteriores do terminal (ClearLines)
  2. Le o AgentProgressState (adquirindo o mutex)
  3. Renderiza o display multi-linha atualizado
4

Dispatch com Eventos

O DispatchWithProgress() executa os agents e envia AgentEvent no channel conforme cada agent inicia e termina.
5

Finalizacao

Após todos os agents terminarem, o timer para, o display e limpo, e os resultados finais são renderizados como cards do timeline.

Tipos de Evento

O dispatcher emite tres tipos de evento via o channel de progresso:
Cada evento carrega contexto completo:

Thread Safety

O AgentProgressState e acessado concorrentemente por duas goroutines:
  1. Consumer goroutine — escreve (via MarkStarted, MarkCompleted, MarkFailed)
  2. Timer goroutine — le (via FormatDispatchProgress)
A sincronizacao e feita por um sync.Mutex interno:
Cada método publico adquire o mutex antes de ler ou escrever. O FormatDispatchProgress também adquire o mutex e faz um snapshot completo do estado antes de formatar a string de saida.
O channel progressCh usa buffer de N×2 (onde N = número de agents) para evitar que workers bloqueiem ao enviar eventos caso o consumer esteja momentaneamente ocupado.

Renderizacao do Terminal

O display multi-linha usa escape sequences ANSI para atualizar o terminal in-place: A cada tick do timer (100ms), o callback:
  1. Emite ClearLines(prevLines) para apagar o display anterior
  2. Chama FormatDispatchProgress() que retorna a string multi-linha atualizada
  3. Imprime a nova string
  4. Atualiza prevLines para o próximo ciclo
A latência máxima entre um evento de mudanca e sua exibicao no terminal e de 100ms — imperceptivel para o usuário.

Interação com Policy Prompts

Quando um worker precisa de aprovação de segurança (policy “ask”), o sistema:
  1. Pausa o timer — o display de progresso para de atualizar
  2. Exibe o prompt de segurança — com contexto do agent
  3. Após resposta — resume o timer e o display continua atualizando
Isso evita que o spinner e o prompt de segurança se sobreponham no terminal.

Spinner labels per-call (DescribeCall)

A linha do spinner usa o método DescribeCall(args) de cada plugin quando disponível, surfaceando o alvo concreto da operação em vez de uma label genérica. Comparação: Plugins legados que não implementam DescriberWithInput continuam mostrando o fallback EXECUTANDO: <tool> <subcmd> (locale-resolved, vira RUNNING: em en). Os tools de ação/multimodais (@send, @moa, @osv, @session, @speak, @image, @skill) implementam DescribeCall, então a caixa mostra um label conciso (ex.: 🎨 Gerando imagem: a watercolor fox) em vez da descrição longa do tool.

Spinner animado durante a execução

Tools que esperam I/O de rede (@moa, @image, @speak, @webfetch, @websearch, @osv, @send…) exibiam uma caixa estática — sensação de travado. Agora o loop do agente roda o spinner braille animado (⠋⠙⠹…) enquanto o tool executa, com o label do DescribeCall. Comportamento:
  • Tool bloqueante (rede): o spinner anima até o retorno.
  • Tool que faz stream (ex.: @coder exec): o spinner para na primeira linha de saída e o output flui normal (sem conflito com o repaint por carriage-return).
  • Fora de TTY (gateway/daemon), o spinner é suprimido automaticamente.

Indicador de fila de mensagens (mid-turn)

Durante o turn do agente o spinner exibe (N na fila) quando há mensagens enfileiradas pelo usuário. O contador soma:
  • Mensagens completadas (Enter pressionado) ainda não drenadas pro messageQueue
  • Mensagens que já caíram no messageQueue aguardando o próximo turn
Resultado: pressionar Enter durante o stream do LLM atualiza o indicador imediatamente em vez de esperar o turn fechar.
Funciona tanto em /agent quanto em /coder (antes só funcionava em /agent).

Ciclo de Vida Completo


Componentes de Código


Comparacao: Antes vs Depois

O usuário só sabia que algo estava acontecendo pelo spinner girando. Para confirmar que os agents estavam de fato rodando, precisava abrir os logs do ChatCLI em outro terminal.