Ravi - Arquitetura do Sistema
Ravi e um sistema multi-agent construido sobre o Claude Agent SDK que orquestra conversas em multiplas plataformas (WhatsApp, Matrix, TUI).
Repositorio: /Users/luis/dev/filipelabs/ravi.bot
Runtime: Bun | DB: SQLite | PubSub: notif.sh | AI: Claude SDK
Fluxo Principal de Mensagens
[WhatsApp/Matrix/TUI]
-> Channel Plugin (normaliza mensagem)
-> Gateway (formata envelope, resolve rota)
-> notif.emit("ravi.{sessionKey}.prompt")
-> RaviBot (Claude SDK query)
-> notif.emit("ravi.{sessionKey}.response")
-> Gateway (roteia para canal)
-> [WhatsApp/Matrix/TUI]
Componentes Core
daemon.ts - Ponto de Entrada
Orquestra startup de todos os subsistemas:
- •Carrega env de
~/.ravi/.env - •Inicia RaviBot (Claude SDK)
- •Cria Gateway com channel plugins
- •Registra WhatsApp + Matrix
- •Inicia HeartbeatRunner, CronRunner, OutboundRunner, TriggerRunner
gateway.ts - Orquestrador de Mensagens
Inbound: Channel normaliza msg -> Gateway resolve rota -> emite prompt Outbound: Bot emite response -> Gateway extrai target -> envia pro canal
Subscriptions importantes:
- •
{channelId}.*.inbound- Msgs dos canais - •
ravi.*.response- Respostas do bot - •
ravi.outbound.deliver- Envio direto do outbound - •
ravi.media.send- Envio de midia
Features:
- •Ghost detection via
_emitId(previne duplicatas de restarts) - •Typing indicator automatico
- •Outbound suppression (msgs de contatos outbound nao geram prompt)
bot.ts - Core do Bot
Processa prompts usando Claude Agent SDK.
Fluxo:
- •Subscribe em
ravi.*.prompt - •Resolve agent config pela session key
- •Debounce se configurado
- •Cria/resume SDK session
- •Streama resposta -> emite responses parciais
- •Gerencia interrupts (msg nova durante processamento)
Interrupt handling:
- •Tool rodando -> enfileira msg
- •Sem tool -> aborta query atual
- •Apos tool terminar -> checa fila -> interrupt
- •Msgs enfileiradas sao combinadas em 1 prompt
Abort control:
- •Cada sessao tem AbortController
- •Novo prompt aborta subprocess anterior
- •Daemon stop aborta TODOS os controllers
- •Watchdog de 5min detecta sessoes travadas
router/ - Sistema de Roteamento
Resolucao de rota (prioridade):
- •Agent atribuido ao contato
- •Rota com pattern match
- •AccountId = AgentId (Matrix)
- •Agent default
Session keys:
agent:{agentId}:{scope}:{peerId}
agent:main:main # Todas DMs (scope=main)
agent:main:dm:5511999 # Por contato
agent:main:whatsapp:group:123456 # Grupo
agent:main:outbound:queueId:phone # Outbound
DM Scopes:
- •
main- Todas DMs compartilham 1 sessao - •
per-peer- Isolado por contato (default) - •
per-channel-peer- Isolado por canal+contato - •
per-account-channel-peer- Isolamento total
Arquivos:
- •
router/resolver.ts- Resolve rota -> session key - •
router/session-key.ts- Constroi/parseia session keys - •
router/sessions.ts- CRUD de sessoes (SQLite) - •
router/config.ts- Carrega config de agents/routes - •
router/router-db.ts- Camada de banco
channels/ - Abstracao de Canais
Interface unificada para plataformas de mensagem.
Adapters:
- •ConfigAdapter - Configuracao de contas
- •SecurityAdapter - Controle de acesso
- •OutboundAdapter - Envio de msgs, typing, reactions
- •GatewayAdapter - Lifecycle de conexao
- •StatusAdapter - Health monitoring
WhatsApp (channels/whatsapp/):
- •Baileys SDK, multi-account, QR pairing
- •Media download com limites de tamanho
- •Transcricao de audio (OpenAI Whisper)
- •JID/LID resolution
- •Sessao em
~/ravi/sessions/whatsapp/{accountId}/
Matrix (channels/matrix/):
- •matrix-bot-sdk, multi-account
- •E2E encryption, device verification
- •Credenciais em
~/ravi/sessions/matrix/accounts.json - •Cada agent pode ter matrixAccount diferente
Subsistemas de Automacao
Outbound (outbound/)
Campanhas de mensagens proativas com processamento round-robin.
Fluxo:
- •Runner pega proxima queue do DB
- •Arma timer para
nextRunAt - •Processa entries por prioridade:
- •Response entries (contato respondeu)
- •Follow-up entries (sem resposta, precisa follow-up)
- •Initial outreach (entries pendentes)
- •Manda prompt para agent com contexto da campanha
Qualificacao: cold -> warm -> interested -> qualified/rejected
Triggers (triggers/)
Automacao event-driven disparada por eventos do sistema.
Fluxo:
- •Runner subscribe em topics configurados
- •Evento dispara -> busca triggers ativos com match
- •Emite prompt para sessao target do trigger
- •Respeita cooldown entre disparos
Cron (cron/)
Jobs agendados com suporte a multiplos formatos.
Schedules: cron expression | interval ("30m") | daily ("09:00") | at (one-time)
Fluxo: Timer -> job due -> emite prompt -> calcula proximo run
Heartbeat (heartbeat/)
Check-ins periodicos de agents dentro de horarios ativos.
Fluxo:
- •Checa agents com heartbeat enabled
- •Para cada sessao, verifica se esta due
- •Checa active hours (timezone-aware)
- •Envia prompt de status check
- •
HEARTBEAT_OK= silencioso, qualquer outro texto = envia pro canal
Cross-Send (cli/commands/cross.ts)
Mensagens entre sessoes (inter-agent communication).
Tipos: relay | inform | execute | ask | answer
Ask/Answer flow:
- •Agent A:
cross_send target ask "pergunta" sender - •Bot emite
[System] Ask:para target - •Agent B responde, usa
cross_send origin answer "resposta" - •Bot emite
[System] Answer:para origin
Sistema de Plugins
Duas fontes:
- •Internos - Embutidos no build, extraidos para
~/.cache/ravi/plugins/ - •Usuario - Customizados em
~/ravi/plugins/
Build time: gen-plugins.ts escaneia src/plugins/internal/ e gera internal-registry.ts
Runtime: discoverPlugins() extrai internos + escaneia user dir -> passa para SDK
Sistema de Hooks
PreToolUse (bash/hook.ts)
Intercepta chamadas Bash e valida contra BashConfig do agent. Modos: bypass | allowlist | denylist
PreCompact (hooks/pre-compact.ts)
Extrai memorias antes do SDK compactar contexto.
Le COMPACT_INSTRUCTIONS.md do agent, roda modelo barato em background, atualiza MEMORY.md.
Infraestrutura CLI
Decorators
@Group({ name: "agents" })
class AgentCommands {
@Command({ name: "list" })
list() { ... }
@Command({ name: "create" })
create(@Arg("id") id: string) { ... }
}
Contexto (AsyncLocalStorage)
runWithContext({ sessionKey, agentId, source }, async () => {
// CLI tools acessam contexto sem parametros
const ctx = getContext();
});
Storage
~/ravi/ravi.db - SQLite: agents, routes, sessions, contacts, settings
~/.ravi/.env - API keys e env vars
~/.ravi/logs/ - Logs do daemon
~/ravi/{agent-id}/ - Diretorios dos agents (CLAUDE.md, MEMORY.md)
~/ravi/sessions/ - Sessoes dos canais (WhatsApp, Matrix)
~/.cache/ravi/plugins/ - Plugins internos extraidos
~/.claude/sessions/ - SDK session files (JSONL)
Padroes Importantes
- •PubSub via notif - Tudo comunica via topics
- •Ghost detection -
_emitId+_instanceIdprevine duplicatas - •Abort control - AbortController por sessao, cleanup no stop
- •Debouncing - Agrupa msgs rapidas por window configuravel
- •Outbound suppression - Msgs de contatos outbound nao geram prompt duplicado
- •Silent token -
@@SILENT@@suprime emissao pro canal - •Session resumption - SDK sessions persistem entre mensagens