Conteúdo detalhado
🗺️ Mapa dos domínios
O diretório src/openhuman/ abriga dezenas de domínios, cada um em sua pasta. Conhecer os principais é o atalho para se localizar em qualquer issue.
📦 Domínios principais
agent orquestradormemory memória persistentechannels entrada multi-canalthreads conversasskills metadatacron schedulerwebhooks eventos externosproviders LLM providerstools tool surfacemeet meet integrationvoice áudiobilling cota / walletcredentials segredos cifradossubconscious consolidaçãotree_summarizer resumos📐 Layout interno do domínio
mod.rs— exports apenas (leve)types.rs— structs do domíniostore.rs— persistênciaops.rs— lógica de negóciorpc.rs— handlers que respondem a RPCschemas.rs— registry de controllersbus.rs— subscribers do event bus (quando aplicável)
⚠️ Regra de pasta
Novos arquivos vão em subdiretório dedicado. Não criar src/openhuman/<coisa>.rs solto. util.rs e dev_paths.rs são grandfathered, não template.
🤖 Agent harness
O src/openhuman/agent/ é onde o LLM ganha mãos e olhos. Ele monta o contexto, expõe ferramentas, controla aprovações e respeita timeouts.
🧠 Componentes do harness
- •Prompts bundled em
src/openhuman/agent/prompts/— também empacotados viatauri.conf.jsonresources. - •Tool surface — quais ferramentas o modelo vê (memory, channels, skills, …).
- •Approval gates (
approval/) — ações sensíveis pedem confirmação. - •Tool timeout — execuções longas não travam o agente.
- •Autocomplete — sugestões durante input do usuário.
💡 Skills runtime foi removido
O QuickJS / rquickjs que executava skills foi retirado. src/openhuman/skills/ hoje é só metadata: discover/install/parse/inject. Não tente "rodar skill" em produção — esse caminho não existe mais.
🧠 Pipeline de memória
Sem memória, OpenHuman seria mais um wrapper. Com ela, é assistente. A pipeline tem três peças que trabalham assíncronas: memory (store), subconscious (consolidação) e tree_summarizer (resumos hierárquicos).
memory — armazena
Embeddings, items, recuperação por similaridade
Recebe novos fatos (mensagens, decisões, fatos sobre pessoas). Indexa via embeddings para busca semântica. Source of truth da memória do agente.
subconscious — consolida
Workers assíncronos em background
Faz pós-processamento: dedup, refinamento, vinculação a entidades, decay. Roda fora do caminho crítico para não engasgar respostas.
tree_summarizer — resume
Hierarquia de sumários
Threads grandes viram resumos de N níveis. O agente carrega o resumo certo para o tamanho de janela disponível, em vez de tudo.
🔍 Por que três peças
Síncrono = lento. Tudo no caminho do request quebraria UX. Separação permite que cada peça evolua e falhe isoladamente (memória continua funcionando se o sumarizador atrasar).
📨 Channels e threads
Não confunda: channels são as origens (Slack, Gmail, WhatsApp); threads são as conversas que agrupam mensagens com contexto compartilhado, podendo cruzar canais.
📡 channels
- ✓Abstração da origem (uma por provedor)
- ✓Recebe via webhook, scraping CDP, polling
- ✓
ChannelInboundSubscriberescuta o bus - ✓Normaliza para evento interno comum
💬 threads
- ✓Agrupamento por contexto, não por canal
- ✓Mantém histórico para o agente
- ✓Pode ter mensagens de múltiplos channels
- ✓Surface em
/chatda UI
🗺️ Fluxo típico de mensagem
Mensagem chega no Slack → scanner CDP captura → publica evento no bus → ChannelInboundSubscriber normaliza → cria/atualiza thread → agente decide se responde → memória atualiza.
📡 Event bus tipado
Em src/core/event_bus/ mora a espinha dorsal de coordenação entre domínios. Tem dois modos: broadcast pub/sub e request/response 1:1 nativo (zero serialização).
📣 Modo 1 — Broadcast tipado
🎯 Modo 2 — Native request/response
🎚️ Domínios do bus
agent, memory, channel, cron, skill, tool, webhook, system. Subscribers ficam em <domain>/bus.rs com convenção <Purpose>Subscriber + name() = "<domain>::<purpose>".
💡 Quando usar cada modo
Broadcast para "aconteceu X, quem quiser ouvir reage". Native r/r para "preciso do resultado de Y agora, sem JSON, sem latência de fan-out". Native é interno — RPC público vai por src/core/all.rs.
🎛️ Controller pattern
Como uma função do core fica disponível para CLI e JSON-RPC ao mesmo tempo, sem duplicação? Via controller registry.
📋 Checklist de migração para controller
✓ Fazer
- ✓Definir
ControllerSchema+FieldSchema - ✓Retornar
RpcOutcome<T>(ver AGENTS.md) - ✓Registrar via
all_registered_controllers - ✓Adicionar teste em
tests/json_rpc_e2e.rs
✗ Evitar
- ✗Adicionar branch em
src/core/cli.rs - ✗Adicionar branch em
src/core/jsonrpc.rs - ✗Duplicar tipos entre CLI e RPC
- ✗Lógica gorda em
mod.rsdo domínio
📖 Leitura obrigatória
O contrato detalhado vive em AGENTS.md (raiz do repo) — RpcOutcome<T>, naming, retornos. Antes de qualquer PR que toque RPC, ler.
🌱 Resumo do Módulo
Próxima trilha:
Trilha 2 — 📦 Instalação (setup local, dev e build de produção)