O Que Você Ve
Durante a execução paralela, o terminal renderiza um display multi-linha atualizado a cada 100ms:Elementos do Display
| Elemento | Descrição |
|---|---|
Spinner (⋮ ⋯ ⋰ ⋱) | Animacao rotativa confirmando que o processo está ativo |
[modelo] | Provedor/modelo LLM em uso pelos workers |
[tempo] | Tempo total decorrido desde o início do dispatch |
| Barra de progresso | Representacao visual da porcentagem concluida |
N/M agents (X%) | Contagem e porcentagem de agents finalizados |
Icones de Status por Agent
| Icone | Status | Descrição |
|---|---|---|
○ | Pendente | Agent aguardando slot no semaforo (max workers atingido) |
⋯ | Executando | Agent em execução ativa (spinner animado) |
✓ | Concluido | Agent finalizou com sucesso (mostra duracao) |
✗ | Falhou | Agent finalizou com erro (mostra duracao + mensagem) |
Arquitetura Interna
O sistema de progresso live usa 3 goroutines independentes comunicando via estado compartilhado protegido por mutex:Fluxo Detalhado
Inicializacao
O
agent_mode.go cria um AgentProgressState com N slots (um por agent) e um channel progressCh com buffer N×2.Goroutine Consumer
Uma goroutine dedicada consome eventos do
progressCh e atualiza o estado compartilhado via métodos Mark*() protegidos por mutex.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
Dispatch com Eventos
O
DispatchWithProgress() executa os agents e envia AgentEvent no channel conforme cada agent inicia e termina.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:| Operação | Escape Sequence | Descrição |
|---|---|---|
| Limpar linha | \r\033[K | Retorna ao início e apaga a linha |
| Subir N linhas | \033[A\033[K (×N) | Move cursor para cima e limpa cada linha |
- 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:
| Plugin | Antes | Depois |
|---|---|---|
@read | EXECUTANDO: @read read | Reading: main.go |
@search | EXECUTANDO: @search search | Searching: golang errgroup |
@webfetch | EXECUTANDO: @webfetch fetch | Fetching: https://api.example.com/... |
@coder exec | EXECUTANDO: @coder exec | Executing: go test ./... |
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
| Componente | Arquivo | Responsabilidade |
|---|---|---|
AgentEvent / AgentEventType | cli/agent/workers/types.go | Tipos de evento de progresso |
DispatchWithProgress() | cli/agent/workers/dispatcher.go | Dispatcher que emite eventos via channel |
AgentProgressState | cli/metrics/display.go | Estado thread-safe de cada agent |
FormatDispatchProgress() | cli/metrics/display.go | Renderizacao do display multi-linha |
ClearLines() | cli/metrics/display.go | Limpeza de linhas do terminal |
| Integração | cli/agent_mode.go | Orquestracao do consumer + timer + dispatch |
Comparacao: Antes vs Depois
- Antes (Spinner Estatico)
- Depois (Progresso Live)