O Que Você Ve
Durante a execução paralela, o terminal renderiza um display multi-linha atualizado a cada 100ms: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:- Limpa as linhas anteriores do terminal (
ClearLines) - Le o
AgentProgressState(adquirindo o mutex) - 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:Thread Safety
OAgentProgressState e acessado concorrentemente por duas goroutines:
- Consumer goroutine — escreve (via
MarkStarted,MarkCompleted,MarkFailed) - Timer goroutine — le (via
FormatDispatchProgress)
sync.Mutex interno:
FormatDispatchProgress também adquire o mutex e faz um snapshot completo do estado antes de formatar a string de saida.
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:
- Emite
ClearLines(prevLines)para apagar o display anterior - Chama
FormatDispatchProgress()que retorna a string multi-linha atualizada - Imprime a nova string
- Atualiza
prevLinespara 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:- Pausa o timer — o display de progresso para de atualizar
- Exibe o prompt de segurança — com contexto do agent
- Após resposta — resume o timer e o display continua atualizando
Spinner labels per-call (DescribeCall)
A linha do spinner usa o métodoDescribeCall(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
messageQueueaguardando o próximo turn
/agent quanto em /coder (antes só funcionava em /agent).
Ciclo de Vida Completo
Componentes de Código
Comparacao: Antes vs Depois
- Antes (Spinner Estatico)
- Depois (Progresso Live)