/coder, com suas tools (arquivos, shell, web, MCP) e a sua memória, enquanto o progresso e a resposta voltam para a conversa.
Conecte um canal
Escolha um canal para ver configuração, variáveis e limites.Iniciar e parar
O gateway roda como daemon destacado:/gateway start lança o chatcli gateway em segundo plano e devolve o prompt na hora, então você continua usando o ChatCLI normalmente.
- Cada canal só liga quando suas variáveis obrigatórias estão definidas, então configure apenas os que quiser. Sem nenhum configurado,
/gateway startavisa e nada roda. - O daemon guarda o pid em
~/.chatcli/gateway.pid(um segundostartcom ele rodando é recusado) e escreve em~/.chatcli/gateway.log, o primeiro lugar a olhar quando um canal fica em silêncio. chatcli gatewayroda o mesmo daemon em primeiro plano, para um gerenciador de serviços ou um container; ele para comCtrl+CouSIGTERM.- Com
CHATCLI_GATEWAY_IN_SERVER=true, o gateway roda dentro dochatcli servere compartilha o Conversation Hub com os clientes conectados.
Como uma mensagem é tratada
- O adaptador recebe a mensagem pela API HTTP da própria plataforma (sem SDKs de terceiros), baixa voice note ou imagem e entrega ao runner. Cada chat é uma conversa, identificada por plataforma e chat.
- Uma voice note é transcrita primeiro, então o agente sempre recebe texto; uma imagem é anexada ao turno para o modelo ver.
- O agente executa o pedido com suas tools e sem confirmações, na voz conversacional do gateway e no idioma da mensagem.
- Enquanto ele trabalha, a pessoa vê que está em andamento: o “digitando…” nativo no Telegram, ou um aviso curto (
gateway.thinking, ”🤔 Recebido — já estou processando…”) nos outros canais, enviado só quando a resposta passa de uns 2 segundos. O progresso das chamadas de tools do agente é agrupado e enviado como mensagens novas no máximo a cada 3 segundos. - A resposta é a resposta final do agente, escrita como mensagem de chat; ”✅” só é enviado se o agente terminou sem uma. Um erro volta como “⚠️” seguido do erro.
Concorrência e contexto
As mensagens são recebidas em paralelo (até 64 na fila, 4 workers), mas as execuções do agente acontecem uma por vez em todos os canais, porque o agente usa estado compartilhado do ChatCLI. Cada turno recebe como contexto os últimos 12 turnos da conversa do remetente no Conversation Hub; o estado durável fica nos arquivos que o agente edita e nas sessões nomeadas.Limites comuns a todos os canais
- Uma resposta é cortada em 3500 caracteres e termina com
…. - São usadas uma voice note e uma imagem por mensagem; os downloads são limitados por
CHATCLI_GATEWAY_MAX_AUDIO_BYTESeCHATCLI_GATEWAY_MAX_IMAGE_BYTES(20 MB cada, por padrão).
Modelo em runtime
O gateway espelha o modelo (e o provider) que a sua sessão interativa está usando — não o default do.env. Trocar de modelo ou de provider na REPL com /switch, /model ou /max-tokens propaga para o daemon: ele relê a escolha antes de cada mensagem, então uma conversa em andamento no Telegram passa a responder com o novo modelo sem reiniciar o gateway.
Como o daemon roda em um processo separado, a sincronização passa por um pequeno arquivo de estado em ~/.chatcli/runtime_model.json que a sessão interativa escreve e o daemon lê. Isso resolve os dois casos: subir o gateway depois de trocar o modelo, e trocar com o gateway já rodando.
Ao trocar de provider, o daemon adota o provider novo já com o modelo correto dele — desde que as credenciais desse provider estejam no ambiente que o daemon herdou (normalmente o seu
.env). Ajustes que vivem só em memória, como /switch --realm / --agent-id do StackSpot, não propagam por esse arquivo; defina-os via variável de ambiente ou reinicie o gateway.Respostas conversacionais (não “tom de coder”)
O gateway usa o mesmo motor do/coder — todas as ferramentas (ler/editar arquivos, shell, web, MCP) continuam disponíveis — mas com uma voz própria, conversacional. A resposta final é a mensagem que a pessoa lê no chat, não um resumo técnico de commit: texto direto e natural, sem tabelas, banners, ASCII art ou blocos de código longos (a menos que peçam código). Internamente isso é um system prompt dedicado do gateway, aplicado no lugar do prompt de coder, preservando a mecânica de tool-use.
Idioma dinâmico (segue quem fala)
A resposta sai no idioma da mensagem do usuário, detectado a cada turno — e não preso à locale do daemon. Português → responde em português; espanhol → espanhol; e assim por diante. A diretiva de idioma dinâmica é aplicada em todos os caminhos do gateway (inclusive com persona ativa), então a resposta nunca fica estática num idioma só. No CLI interativo, a diretiva fixa por locale (CHATCLI_LANG) continua valendo — quem muda é só o gateway.
Exemplo de uso
111111111 manda:
“liste os arquivos Go alterados no último commit e resuma o diff”O bot mostra “digitando…”, manda o progresso enquanto o agente roda
git e lê os arquivos, e termina com o resumo escrito como mensagem de chat. Uma mensagem de quem não está em CHATCLI_TELEGRAM_ALLOWED_USERS não recebe resposta.
Mensagens de voz (transcrição)
O gateway aceita voice notes e áudio em todos os canais. A mensagem é transcrita para texto antes do pipeline — então funciona com qualquer um dos 14 providers de chat (eles só veem texto; não exige modelo multimodal nem redesenho de mensagem). O adapter baixa a mídia, transcreve, e o agente trata como um pedido em texto normal — a transcrição é inclusive gravada no Conversation Hub.Backend de transcrição (zero-config, local-first, keyless)
A seleção segue local/sem-chave primeiro — e desde a v1.135 tem um piso embutido: sem nada configurado, o gateway usa o Whisper embarcado (multilíngue, via sherpa-onnx — o mesmo engine do TTS Kokoro), sem API key e sem cgo. O daemon pré-baixa engine + modelo no startup, então a primeira nota de voz já chega com tudo pronto.CHATCLI_TRANSCRIPTION_CMD— um comando STT local seu (qualquer wrapper). Lê o transcript do stdout, ou do.txtescrito em{output_dir}.CHATCLI_TRANSCRIPTION_URL— endpoint OpenAI-compatível self-hosted (whisper.cppwhisper-server, faster-whisper, Speaches). Keyless (a menos queCHATCLI_TRANSCRIPTION_KEY).- Whisper embarcado já provisionado — se o cache (
~/.cache/chatcli/stt/) já tem engine + modelo, ele vence qualquer chave de cloud. - whisper CLI no PATH — se houver
whisper(openai-whisper) ouwhisper-cli(whisper.cpp), é usado automaticamente, com zero config. O modelo ggml é baixado uma vez para o cache (~/.cache/chatcli/whisper/), como o faster-whisper faz. GROQ_API_KEY→ Groq Whisper (free tier).OPENAI_API_KEY→ OpenAI Whisper.- nada configurado → Whisper embarcado: download único (engine ~25MB + modelo
base~200MB) na subida do daemon. Só plataformas sem engine prebuilt (fora de Linux/macOS/Windows x64/arm64) caem na dica de configuração.
CHATCLI_TRANSCRIPTION_PROVIDER fixa um backend (embedded|command|url|groq|openai) — =embedded força o motor embarcado mesmo com whisper/keys presentes. CHATCLI_TRANSCRIPTION_MODEL escolhe o tamanho do modelo embarcado (tiny|base|small|medium|large-v3, default base) ou o modelo cloud; _LANG fixa o idioma (default: auto-detecção do idioma falado); CHATCLI_TRANSCRIPTION_CACHE_DIR realoca o cache (path absoluto — útil para pré-seed air-gapped); CHATCLI_GATEWAY_MAX_AUDIO_BYTES limita o tamanho do download (default 20MB). O backend ativo aparece em /config integrations.
Voice notes são OGG/Opus. O motor embarcado decodifica WAV e OGG/Opus sozinho, então voice notes do Telegram, WhatsApp e Discord funcionam sem instalar mais nada; ele só precisa de ffmpeg para MP3, M4A/AAC, FLAC e WMA. Um whisper.cpp local não decodifica Opus e precisa de ffmpeg, que o gateway então usa para converter para WAV 16 kHz automaticamente. Backends cloud e self-hosted decodificam no servidor. O idioma é detectado pelo áudio, então a transcrição, e a resposta, seguem o idioma falado.
Setup rápido
Zero-config (Whisper embarcado — recomendado):/gateway stop && /gateway start) e mande um áudio.
Respostas em voz
No Telegram o caminho de volta também fala: por padrão (CHATCLI_GATEWAY_VOICE_REPLY=auto) uma voice note recebe voice note e texto recebe texto, com qualquer backend TTS, incluindo o motor embarcado Kokoro (offline, sem API key). Cada conversa liga ou desliga em linguagem natural (“responde em áudio”, “para de mandar áudio”) pela tool @voice, e a escolha fica salva por conversa. Os outros canais respondem em texto: o gateway não sintetiza áudio para eles. Detalhes em Respostas em voz.
Imagens
Uma foto ou arquivo de imagem numa mensagem é anexado ao turno para o modelo ver, com visão nativa quando o modelo suporta; uma imagem enviada sem texto recebe uma instrução padrão para descrevê-la. Quando o agente gera uma imagem durante o turno, a primeira volta com a resposta em todos os canais; definaCHATCLI_GATEWAY_IMAGE_REPLY=never para enviar só texto.
O gateway também trata o índice de memória do usuário como conhecimento real: perguntas pessoais (“o que você sabe sobre mim?”) consultam a memória persistente via @memory recall antes de qualquer “não sei”.
Continuidade cross-channel
Quando o Conversation Hub está ativo (padrão), o gateway compartilha a conversa com o chatcli do seu notebook: um assunto começado no Telegram continua no terminal e vice-versa. Cada mensagem recebida resolve o principal do remetente, lê o contexto recente e grava o turno no hub — então o que você falou no notebook aparece como contexto no Telegram, sem configurar nada (modo single-user). Para push em tempo real ao CLI conectado, rode o gateway dentro do servidor comCHATCLI_GATEWAY_IN_SERVER=true. Bots multi-usuário usam CHATCLI_HUB_ISOLATE=true + bindings. Detalhes em Conversation Hub.
Vínculo de sessão pelo canal
Por cima do hub efêmero, usuários do canal podem vincular a conversa a uma sessão nomeada salva — a camada durável e cross-surface — enviando comandos/session no chat:
Enquanto vinculado, o contexto do turno vem do arquivo da sessão nomeada — que carrega os turnos que outras superfícies (REPL do terminal, servidor MCP/ACP, outro canal) gravaram via write-through — e cada turno concluído do gateway é acrescentado a esse mesmo arquivo. Ou seja:
/session attach projeto-x no WhatsApp continua exatamente a conversa que você começou com /session attach projeto-x no terminal ou na IDE. O binding é por principal (remetente) e persistido nas runtime settings do hub, então sobrevive a restarts do daemon.
/session delete deliberadamente não é exposto aos canais: uma conversa de gateway pode ser multi-usuário, e destruir estado do store continua sendo decisão de operador/REPL. E uma mensagem que apenas começa com / mas não é um comando de sessão flui para o modelo como texto normal do usuário — o input nunca é sequestrado.Veja também
Conversation Hub
Uma conversa só entre os canais e o seu terminal.
Mensagens proativas
Deixe o agente mandar a primeira mensagem num canal com
@send.Gerenciamento de sessões
Sessões nomeadas compartilhadas por todas as superfícies.
Variáveis de ambiente
Todas as configurações do gateway numa tabela.