Skip to content

Repository files navigation

🤖 DEILE — Development Environment Intelligence & Learning Engine

DEILE

Version Python License

Um agent harness multi-provedor — e a plataforma de orquestração autônoma construída sobre ele — para desenvolvimento de software.

O DEILE tem duas faces que compartilham o mesmo núcleo Python:

  1. 🧑‍💻 CLI interativo — você conversa em linguagem natural no terminal e ele lê, escreve e edita arquivos, roda comandos, executa testes, busca no repositório, planeja tarefas e acompanha custo — tudo no diretório de trabalho atual.
  2. ☸️ Pipeline autônomo em Kubernetes — uma frota de pods que monitora um repositório GitHub ou GitLab, refina issues, implementa em branches auto/issue-N, abre PRs/MRs e revisa-as sem intervenção humana.

🧩 Onde o DEILE se encaixa

No núcleo, o DEILE é um agent harness — o runtime que transforma uma API de LLM em agente de verdade: loop de tool-calling, roteamento com fallback, streaming, memória e permissões. É a mesma categoria de Claude Code, aider ou Codex CLI — só que sobre 4 provedores em vez de um.

Em volta desse harness há uma plataforma de orquestração autônoma (o pipeline + a frota K8s) que coordena agentes em paralelo sobre um forge. E aqui está o detalhe que define a categoria: o DEILE pode dirigir outro harness — o pod claude-worker executa claude -p (o próprio Claude Code) como um dos workers despacháveis por etapa do pipeline, lado a lado com o deile-worker (o motor próprio do DEILE). Ou seja: harness no núcleo, meta-harness/orquestrador na borda.

Este README descreve apenas o que existe no código. Cada afirmação foi conferida na fonte. Onde algo é experimental, parcial ou um gap conhecido, está sinalizado.


🗺️ Índice

Seção O quê
🚀 Visão geral O que o DEILE faz, para quem
⚡ Quick start Subir o CLI em minutos
🧰 Ferramentas As tools que o agente aciona
📊 Comandos slash O REPL e suas flags CLI
🌐 Provedores & modelos LLMs, tiers, fallback
🧠 Reasoning effort Esforço de raciocínio por etapa
🎭 Personas · 🧩 Skills · 🧠 Memória Componentes plugáveis
🧩 Sub-DEILEs paralelos Decomposição concorrente
🤖 Pipeline autônomo A máquina de estados de issue→PR
🌳 Forge GitHub + GitLab Forge-agnóstico
☸️ Stack Kubernetes Os 6 pods, deploy.py, painel
🔭 Observabilidade OTLP, runtime state, status servers
🔒 Segurança · 💾 Persistência Permissões, custo, SQLite
🏗️ Arquitetura · ⚙️ Configuração Camadas e settings
📋 Requisitos · 🧪 Testes · 🚦 Operação Engenharia
⚠️ Limitações · 🤝 Contribuir Realidade & contribuição

🚀 Visão geral

DEILE pensa, decide e resolve: aciona ferramentas reais (function calling) para entender o problema, planejar e concluir o que foi pedido, mostra o que está fazendo em tempo real (streaming) e mantém memória da conversa entre turnos. Funciona em português ou inglês.

🎯 Para quem é

Perfil O que ganha
👩‍💻 Pessoa desenvolvedora Um par de programação que executa, não só sugere
🛠️ Engenharia de plataforma Automação de tarefas repetitivas dentro do repositório
🔍 Revisão de código Leitura guiada do projeto em linguagem natural
🤖 Operação autônoma Frota que processa issues → PRs/MRs 24/7 num cluster
🎓 Aprendizado Observar passo a passo como um agente decide e usa ferramentas

✨ O que o DEILE faz hoje

Capacidade Descrição
💬 Conversa multi-turno Contexto e histórico de sessão persistentes
🖼️ Streaming UI Resposta em streaming com renderização incremental de Markdown no terminal
🔁 Loop de ferramentas Function calling iterativo até concluir a tarefa (limite configurável)
🧩 Sub-DEILEs paralelos Decompõe pedidos complexos em N sub-agentes em paralelo, com painel ao vivo
🛠️ Edição de código Lê, cria, edita, deleta e busca arquivos no repositório
⚙️ Execução local Shell/Python, instala pacotes, roda testes
🌐 Roteamento LLM Roteia entre 4 provedores com fallback e seleção por tier
🧠 Reasoning effort Esforço de raciocínio ajustável (lowultracode), global e por etapa do pipeline
🧠 Memória Quatro camadas: working, episodic, semantic, procedural
📋 Orquestração Planeja tarefas, gerencia dependências, workflows com rollback
⌨️ Comandos slash ~40 comandos no REPL; vários também expostos como flag CLI
🎭 Personas Comportamento via Markdown + YAML, sem mudar Python
🧩 Skills Unidades de expertise em Markdown com hot-reload e 4 gatilhos de auto-injeção
🤖 Pipeline autônomo Issue → refino → implementação → PR → review → merge, em GitHub ou GitLab
💰 Telemetria Tokens, latência e custo em USD com persistência SQLite + ledger durável
🔭 Observabilidade OpenTelemetry (traces/métricas) + runtime state por processo + painel TUI
🔒 Segurança Permissões, aprovação por risco, auditoria tipada e scanner de segredos

⚡ Quick start

Pré-requisitos: Python 3.9+ e ao menos uma chave de API entre Anthropic, OpenAI, DeepSeek e Gemini.

🧭 Cobertura por chave: OPENAI_API_KEY ou DEEPSEEK_API_KEY cobrem todas as tiers (1–4). GOOGLE_API_KEY cobre tiers 1–3. ANTHROPIC_API_KEY cobre tiers 1–3. Para cobertura plena e fallback entre provedores, use pelo menos duas chaves.

1️⃣ Clonar

git clone https://github.com/elimarcavalli/deile.git
cd deile

2️⃣ Início rápido (recomendado)

O próprio deile.py faz o setup na 1ª execução: cria .venv, pergunta as chaves de API (input oculto), gera o .env, instala dependências e sobe a CLI. Nas execuções seguintes, detecta o ambiente e inicia direto.

python3 deile.py

3️⃣ Início manual

python3 -m venv .venv
source .venv/bin/activate              # macOS/Linux  (.venv\Scripts\activate no Windows)
pip install -r requirements.txt
cp .env.example .env                   # ~540 linhas, seções comentadas
# preencha ao menos uma chave: ANTHROPIC_API_KEY / OPENAI_API_KEY / DEEPSEEK_API_KEY / GOOGLE_API_KEY / OPENROUTER_API_KEY
python3 deile.py

Use /help para listar os comandos. Para uma única mensagem (one-shot), passe o prompt como argumento.

⚠️ Compatibilidade: homologado para Unix-like (Linux/macOS). Windows pode funcionar, mas é experimental (o status server por Unix-socket e o flock viram no-op).

🌍 Instalar globalmente (a partir do clone local)

python3 deile.py --install                          # pergunta o modo (global/local) e instala
python3 deile.py --install --install-mode global    # venv isolado em ~/.deile/venv/ (pula o prompt)
python3 deile.py --install --install-mode local     # venv no próprio repo (<repo>/.venv/)

O --install cria um venv isolado (não toca o Python do sistema — contorna o PEP 668 sem --break-system-packages), instala o DEILE editável lá dentro e cria um shim deile em ~/.local/bin/ (tentando ajustar o PATH no rc do shell). Depois:

deile                                   # REPL interativo
deile "resuma a arquitetura do repo"    # one-shot
deile --version                         # versão
deile --status                          # painel de saúde (não exige API key)
deile --tools                           # lista tools registradas
deile --model-list                      # tabela de modelos
deile --pipeline-status                 # status do pipeline autônomo
deile --help                            # TODAS as flags + catálogo de slash commands

Vários comandos slash têm uma flag CLI correspondente, gerada automaticamente a partir do CommandRegistry (issue #126) — declarar cli_flag = "--foo" na classe do comando cria a flag (nem todos os ~40 comandos expõem uma).


🧰 Ferramentas integradas

O LLM aciona ferramentas reais via function calling. O conjunto-padrão é auto-descoberto pelo ToolRegistry (deile/tools/discovery.py); demais tools são registradas explicitamente.

📁 Arquivos & busca

Tool Função
read_file Ler arquivo (encoding, limites de tamanho)
write_file Escrever arquivo de forma atômica
edit_file Edição cirúrgica (substituição de trecho)
delete_file Remover arquivo com política
list_files Listar arquivos em diretório
find_in_files Buscar padrões na árvore do projeto

⚙️ Execução

Tool Função
bash_execute Executar comando shell (com níveis de segurança próprios)
python_execute Executar bloco/arquivo Python
pip_install Instalar dependência Python
run_tests Rodar a suíte de testes
vision_describe_image Descrever/analisar uma imagem

🤖 Orquestração & autonomia

Tool Função
dispatch_parallel_subagents Decompõe o pedido em 2–5 sub-DEILEs paralelos (sessões limpas)
dispatch_deile_task Despacha uma tarefa a outro DEILE (worker)
worktree Cria/gerencia git worktrees isolados
pipeline Controla o pipeline autônomo (start/stop/status)
pipeline_schedule Agenda ações do pipeline (recorrente/one-shot, cron)
cron_create · cron_list · cron_delete Agenda prompts naturais via cron genérico

🧠 Skills & preferências

Tool Função
list_skills Catálogo machine-readable das skills carregadas
invoke_skill Carrega o body de uma skill por nome (sob demanda)
remember_preference · list_preferences · forget_preference Memória de preferências do usuário

📨 Mensageria (opcional — só com deilebot instalado)

discord_send_message, discord_send_dm, discord_edit_message, discord_react, discord_pin_message, discord_start_thread, discord_mention_role, discord_get_user_profile, whatsapp_send_template.

🔒 Tools de DM e menção de cargo são SecurityLevel.DANGEROUS e passam pelo ApprovalSystem por design. Elas só se registram quando import deilebot funciona e DEILE_BOT_ENDPOINT + DEILE_BOT_AUTH_TOKEN estão configurados.


📊 Comandos slash

Todos em deile/commands/builtin/. Aliases entre parênteses.

Grupo Comandos
🧭 Sessão /help · /status · /welcome · /context · /compact · /clear (/cls) · /stop · /rename · /resume · /rewind (/rw) · /fork
💰 Custo & métricas /cost · /loc (/estatisticas) · /standup
🧠 Config & runtime /config · /settings · /env · /model · /reasoning (/effort) · /memory · /permissions · /debug
🛠️ Trabalho /tools · /skills · /plan · /run · /todo · /diff · /patch (/patch-generate) · /apply (/patch-apply) · /approve · /logs · /export
🤖 Pipeline & cluster /pipeline · /pipeline-schedule · /backlog · /pods · /panel
ℹ️ Outros /sandbox · /version (/ver)

Além dos built-ins, toda skill carregada vira /<name> automaticamente (exceto as bundled em deile/skills/library/, que ficam só via auto-trigger e invoke_skill). Skills de ~/.claude/commands/ registram em UPPERCASE.


🌐 Provedores de LLM, roteamento e modelos

Definição em deile/config/model_providers.yaml. Provedores só são registrados quando a respectiva chave de ambiente está setada (bootstrap_providers() em deile/core/models/bootstrap.py).

Provider Classe Chave de ambiente SDK / endpoint
Anthropic AnthropicProvider ANTHROPIC_API_KEY anthropic
OpenAI OpenAIProvider OPENAI_API_KEY openai
DeepSeek DeepSeekProvider ⟵ estende OpenAIProvider DEEPSEEK_API_KEY openai (endpoint api.deepseek.com/v1)
Gemini GeminiProvider GOOGLE_API_KEY google-genai
OpenRouter OpenRouterProvider ⟵ estende OpenAIProvider OPENROUTER_API_KEY openai (endpoint openrouter.ai/api/v1)

🌐 OpenRouter (Decisão #50) é um gateway OpenAI-compatible: uma chave dá acesso a todos os modelos (slug openrouter:vendor/model, com / no model_id). O custo é autoritativo — vem do campo usage.cost da própria resposta (extra_body={"usage":{"include":true}}), com a tabela do YAML só como fallback (sem undercount). ⚠️ Privacidade: o prompt é roteado a provedores terceiros (hop documentado no YAML).

🧮 Roteamento — dois roteadores coexistem

  • ModelRouter (router.py) — roteador legado baseado em estratégia.
  • TierRouter (tier_router.py) — roteamento por tier (tier_1 complexo → tier_4 bulk/barato), com um circuit breaker por provider injetado (CircuitBreaker, estados em BreakerState) e fallback automático na cascata do tier.
  • Estratégias (routing_strategies.py): task_optimized (default) e cost_optimized, além de round_robin / least_busy.
  • Budget guard (BudgetGuard em deile/storage/usage_repository.py) — enforcement de orçamento por sessão e por provider (diário e mensal).

📚 Catálogo de modelos (verificado em model_providers.yaml)

Provider Modelos (tier)
Anthropic claude-opus-4-8 (1) · claude-sonnet-4-6 (2) · claude-haiku-4-5 (3)
OpenAI gpt-5.5 (1) · gpt-5.4 (2) · gpt-5.4-mini (3) · gpt-5.4-nano (4)
Gemini gemini-3.1-pro-preview (1) · gemini-2.5-pro (1) · gemini-3.5-flash (2) · gemini-3-flash-preview (2) · gemini-3.1-flash-lite · gemini-2.5-flash · gemini-2.5-flash-lite
DeepSeek deepseek-v4-pro (1) · deepseek-v4-flash (3)

Cada entrada do YAML declara pricing (input/output/cached por 1M tokens) e context_window — base para a telemetria de custo. O nome do modelo precisa ser válido no SDK do provider.


🧠 Reasoning effort (esforço de raciocínio)

Fonte única em deile/core/models/reasoning.py. O DEILE adota o vocabulário do Claude Codelow · medium · high · xhigh · max · ultracode · auto — e o traduz para o parâmetro nativo de cada provider (Anthropic effort, OpenAI reasoning_effort, Gemini thinking_config, DeepSeek). A tradução é fail-open: nunca quebra o turno.

Configurável em três níveis: global (DEILE_REASONING_EFFORT / /reasoning no CLI), por etapa do pipeline (DEILE_PIPELINE_REASONING_<STAGE>) e na coluna Reasoning do painel TUI.

🔒 Hard override por sessão (/reasoning use <nível>): grava forced_reasoning_effort no contexto da sessão e vence a injeção per-turn do worker, estabelecendo a precedência forced > session > global > provider default. /reasoning use clear remove o override (idempotente); /reasoning use auto força o default do provider (não é clear). O slot soft (/reasoning <nível>) segue inalterado.


🎭 Sistema de personas

Personas são MD-driven: editar o Markdown muda o comportamento, sem tocar em Python.

  • 📚 Library (deile/personas/library/*.yaml): analyst, architect, debugger, developer, reviewer (nome/id/capacidades).
  • 📝 Instruções (deile/personas/instructions/*.md): analyst, architect, debugger, developer, reviewer, discord_developer, monitor, monitor_qa, fallback (a prosa que entra no system prompt).

O pipeline escolhe a persona pelo tipo da issue/etapa — analyst refina intents, architect arquiteta features/refactors, debugger investiga bugs e reviewer revisa PRs. A persona monitor fica fora desse despacho por tipo: supervisiona o namespace K8s na Fase B do pod deile-monitor (o tick mecânico do pipeline é determinístico, sem LLM).


🧩 Sistema de skills

Skills são unidades composáveis de expertise em Markdown puro (sem código Python). O loader varre as fontes abaixo em ordem de prioridade crescente — em colisão de nome o source mais alto vence (INFO log):

Origem Caminho Comportamento
Bundled deile/skills/library/**/*.md Vai no pacote; PR no repo. Bundled out-of-the-box: python, typescript, tdd
Usuário pessoal ~/.deile/skills/*.md Visível em qualquer projeto seu
Usuário (Claude compat) ~/.claude/commands/*.md Nome registrado em UPPERCASE (kind=command)
Projeto <cwd>/.deile/skills/*.md Versionada no git, viaja com o repo
Projeto (Claude compat) <cwd>/.claude/commands/*.md UPPERCASE (kind=command)
Configurada library_paths: em deile/config/skills.yaml ou /skills add Paths extras (escopo global/project)

Como o LLM usa cada skill (três caminhos independentes)

  • Auto-injeção no system prompt quando um trigger casa (até max_per_turn=4, ordenadas por (-priority, name)):
    • file_globsfnmatch no basename/path
    • code_block_langs — fence ```python no input (case-insensitive)
    • keywords — match literal (escapado) com word-boundary, case-insensitive (não confunde "rust" com "trust")
    • file_content_patterns — regex em 4 KiB de cada arquivo referenciado, contido ao project_root (segurança)
  • Function-calls invoke_skill(name) e list_skills — o LLM puxa skills que não dispararam por trigger.
  • Slash /<name> — invocação explícita pelo usuário, com argumentos opcionais.

🔁 Hot-reload via watchdog: dropar/editar/remover um .md reflete em ~0,5 s, sem restart (swap atômico no SkillRegistry).


🧠 Camadas de memória

Quatro camadas em deile/memory/, agregadas via MemoryManager (memory_manager.py). A regra: estado cross-turn não vive em globals de módulo — vai para a camada certa.

Camada Módulo Propósito
Working working_memory.py Estado transitório do turno (TTL)
Episodic episodic_memory.py Eventos da sessão — persistido em SQLite (aiosqlite, episodes.db)
Semantic semantic_memory.py Fatos/conhecimento de longa duração
Procedural procedural_memory.py Padrões/skills aprendidos

Nenhuma camada guarda segredos ou PII; toda escrita é assíncrona. A consolidação entre camadas fica em memory_consolidation.py.


🧩 Sub-DEILEs paralelos (decomposição em sessão CLI)

Numa conversa interativa, o DEILE pode identificar sub-tarefas independentes e substanciais e dispará-las em paralelo — cada uma num sub-DEILE com sessão limpa (contexto/histórico próprios). Você vê o progresso ao vivo num painel multipanel.

✅ "Refator módulo A E módulo B (não-acoplados)" · "Gere testes pra X, Y e Z" ❌ Tarefas sequenciais · micro-tarefas (<30s) · mesmo arquivo

O LLM chama dispatch_parallel_subagents com 2–5 sub-tarefas. O SubAgentOrchestrator dispara em paralelo (semaphore DEILE_SUBAGENT_MAX_PARALLEL, default 3), respeitando o budget global. Cada sub-DEILE vai direto ao tool-loop, então o painel mostra ⚙ bash_execute(...), ✓ write_file: 412 bytes, ✎ texto-em-curso em tempo real.

🧩 Decomposto em 2 frentes paralelas · 1 ok · 1/2 concluídas · 00:08

╭─ ▶ sub-DEILE #1 · refatorar auth.py ──────────────────────────── 00:08 ──╮
│ ⚙ bash_execute(pytest deile/tests/auth -q)                               │
│ ✓ bash_execute: 5 passed in 0.04s                                        │
│ ✎ aplicando guard clauses (3/5 funções)                                  │
╰──────────────────────────────────────────────────────────────────────────╯
╭─ ✅ sub-DEILE #2 · doc do módulo X ──────────────────────────────────────╮
│ ✅ concluído · docs/x.md                                                  │
╰──────────────────────────────────────────────────────────────────────────╯
(toque 1-9 para focar · ESC: fecha painel)

Runners pluggable: LocalSubAgentRunner (default, in-process via asyncio) ou WorkerSubAgentRunner (delega ao deile-worker por HTTP — DEILE_SUBAGENT_RUNNER=worker). Falha de uma frente não cancela as siblings; o resumo final é gravado no histórico e o /resume reconstrói o painel.

Variável Default Uso
DEILE_SUBAGENT_RUNNER local local | worker
DEILE_SUBAGENT_MAX_PARALLEL 3 Teto de concorrência por chamada
DEILE_SUBAGENT_BUDGET_S 600 Teto global de tempo (s)
DEILE_SUBAGENT_POLL_INTERVAL_S 0.8 Polling do worker runner

🤖 Pipeline autônomo (de issue a PR)

Quando uma issue recebe ~workflow:nova (ou o bot é atribuído/mencionado), o pipeline a leva sozinho até a PR/MR — refinando o escopo antes de escrever uma linha de código. Roda no pod deile-pipeline (PipelineMonitor.tick() em loop).

🔁 Máquina de estados de issues

🆕 ~workflow:nova
      │
      ▼  Stage 1 — crítica de escopo (persona por tipo: analyst/architect/debugger)
🔍 ~workflow:em_revisao
      │
      ├─ VEREDITO: CLARO ──────────────► ✅ ~workflow:revisada
      │                                        │
      │                            ┌───────────┴────────────┐
      │                         (code-type)             (intent)
      │                            ▼                        ▼
      │                    🚀 em_implementacao        🧩 decomposta
      │                            ▼                  (architect abre N derivadas)
      │                    📬 em_pr  →  PR/MR aberta
      │
      └─ VEREDITO: VAGO ─► 🏷️ refinar + estado de refino:
                              🧠 em_refinamento  (intent → analyst)
                              🏛️ em_arquitetura  (feature/bug/refactor → architect/debugger)
                                     │
                                     ↕ ⏸️ aguardando_stakeholder (humano remove p/ retomar)
                                     │
                              volta p/ 🆕 ~nova  (até 5 voltas; estourou → ⛔ bloqueada)
  • Portão de refinamento — toda issue nova é criticada por escopo por uma persona escolhida pelo tipo: intent → analyst, feature/refactorarchitect, bug → debugger. Issues VAGAS ganham refinar + estado de refino e são reescritas (corpo, comentários, bracket [TIPO] do título) até 5 voltas (contador durável via label ~refine:N). Um gap de alto impacto pode pausar em ~workflow:aguardando_stakeholder com 2–3 opções sugeridas — o humano remove a label para retomar.
  • Decomposição — intents que passam são quebrados em issues derivadas pelo architect (default anti-flood: agregar numa única issue com checklist - [ ]; só dividir com independência provada).
  • PRs/MRs~review:pendente~review:em_andamento~review:concluida.
  • Estado terminal & GC~workflow:concluida é aplicada automaticamente pelo GC (run_terminal_gc) a issues fechadas (idempotente, nunca manual — issue #587).
  • Locks & marcadores~batch:<sha8> (claim distribuído, só quando há mais de um monitor); ~by:<id> (dono do claim); ~mention:processado (idempotência de menção); ~follow_ups:processed (idempotência do estágio de follow-ups num PR mergeado); ~workflow:bloqueada (bloqueio duro, exclui do auto-resume).
  • Contadores duráveis (sobrevivem a restart) — ~refine:N (voltas do portão de refinamento, teto 5); ~attempt:N (re-claims do reaper sobre trabalho stuck — em N ≥ reaper_max_attempts, default 3, bloqueia em vez de liberar); ~prioridade:0..3 (reordena a seleção de candidatos — menor primeiro; sem label = por último; PRs herdam da issue vinculada).

🧭 As 5 etapas — cada uma roteável de forma independente

classify · refine · implement · pr_review · follow_ups. Para cada etapa você escolhe três eixos independentes (com fallback global e persistência por env var ou ~/.deile/settings.json):

Eixo Por etapa Global Onde
👷 Worker DEILE_PIPELINE_DISPATCH_<STAGE> DEILE_PIPELINE_DISPATCH_MODE (built-in default deile-worker) dispatch_resolver.py
🧠 Modelo DEILE_PIPELINE_MODEL_<STAGE> DEILE_PREFERRED_MODEL model_resolver.py
🤔 Reasoning DEILE_PIPELINE_REASONING_<STAGE> DEILE_REASONING_EFFORT reasoning_resolver.py

📌 O default built-in do resolver é deile-worker, mas o manifest do pipeline (46-deile-pipeline-deployment.yaml) agora versiona DEILE_PIPELINE_DISPATCH_MODE=claude (all-claude-worker) + DEILE_PIPELINE_MAX_PARALLEL=2 como default em produção — antes esse routing vinha de kubectl set env e qualquer kubectl apply o resetava (causando drift/CrashLoop). max_parallel=2 alinha ao cap de concorrência do claude-worker (acima disso só gera 409 + churn de label).

Cada etapa aponta para um worker da frota:

  • 🐍 deile-worker (:8766) — roda o DEILE Python in-process, usando seus próprios provedores de LLM.
  • 🤖 claude-worker (:8767) — roda o claude -p (Claude Code CLI) em worktrees isolados sob PVC; auth via CLAUDE_CODE_OAUTH_TOKEN (token de ~1 ano gerado por claude setup-token, injetado por env var a partir de Secret — issue #603, sem credentials.json/initContainer).
  • 🧩 Frota de CLI workers plugáveis — além dos dois núcleos, qualquer CLI de codificação vira um worker despachável escrevendo um adapter (infra/k8s/cli_adapters/<kind>.py, que satisfaz o Protocol CliAdapter); o auto-discovery monta ADAPTERS como fonte única que dirige resolver, painel e geração de manifest/NetworkPolicy — nenhum consumidor é editado (ver Decisão #51). Cada worker é um pod com Service próprio (portas derivadas do default_port do adapter), nasce replicas:0 (scale-to-zero) e é escalado 0→1 sob demanda (cli_worker_scaler.py). Server genérico infra/k8s/cli_worker_server.py reusa _worker_core.py (lease/heartbeat/subprocess/gate de commit+push+test). Resume nativo no mesmo workdir (sem re-gastar tokens) quando o adapter declara supports_resume; o custo de cada sessão é empurrado centralmente para o UsageRepository (SQLite central) pelo pipeline via o bloco usage da resposta do /v1/dispatch (issue #638 — sobrevive ao scale-to-zero), com fallback no ledger durável por-PVC; a tela [T]okens do painel lê o store central primeiro.

📌 Roteamento de menção/atribuição (process_mentions é um roteador)

Trigger Ação
Issue + assignee/menção no corpo Injeta ~workflow:nova → o pipeline assume
Qualquer trigger sobre uma PR/MR Brief unificado pr_unified: o worker abre a PR, descobre o estado real (papel autor/assignee/reviewer; HEAD vs último review; threads abertas; comentários dirigidos a mim) e age — revisa, comenta, atende thread ou mergeia. PR open → push direto; merged/closed → branch derivada auto/<orig>-followup-<sha> + nova PR
Issue + comentário Faz o que o comentário pede (one-shot sob persona developer); se a issue está num gate ativo, o próprio gate relê o comentário

🔒 Quality-gate (adaptativo ao CI, #85): o brief unificado de PR detecta se o repo-alvo tem CI e adapta o portão de regressão — com CI, o revisor exige todos os checks 100% verdes (corrige e dá push até passar) e não roda a suíte in-pod (o CI é o portão); sem CI, ele roda a suíte completa ele mesmo (python3 -m pytest deile/tests/ -q). Além disso, o estágio review_one_open_pr consulta forge.get_ci_status(pr) antes de despachar: com o CI ainda pending, pula o dispatch neste tick (sem gastar tentativa) e reconcilia no próximo — evitando redispachar a review a cada ~60s enquanto o CI roda por minutos. O implementador roda só os testes impactados (-p no:cov) e delega a suíte/CI ao revisor. O brief confronta entrega vs pedido (claim-vs-código, completude vs ACs, qualidade dos testes, escopo, segurança, design) — verde não substitui requisito faltante.

♻️ Resume sob demanda

Despacho fresh é o default; resume só é solicitado (resume=True) quando há trabalho-em-curso real registrado no DispatchLedger (anti-double-dispatch). Sessões claude -p acima do orçamento de tokens (DEILE_CLAUDE_RESUME_TOKEN_BUDGET, ~100K) são promovidas para fresh em vez de rejeitadas. Falhas de auth recorrentes entram em backoff exponencial por target. O brief lê .deile-progress.md no PASSO 0 — então "fresh com contexto natural" cobre a maioria dos casos sem inflar o JSONL da sessão.

🏁 Quick start do pipeline (REPL)

export DEILE_FORGE_REPO=owner/repo          # env var canônico do repositório (no settings.json: pipeline.repo)
export DEILE_PIPELINE_BASE_PATH=/caminho/para/repo
> /pipeline start          # inicia o loop de polling
> /pipeline status         # contadores
> /pipeline stop           # para

Ou DEILE_PIPELINE_AUTOSTART=1. Agendamento via pipeline_schedule(...) (recorrente/one-shot, cron) e cron_create(prompt=..., cron=...).


🌳 Forge — GitHub & GitLab

O DEILE é forge-agnóstico (issue #297). O mesmo agente, pipeline e briefs operam idênticos em GitHub (cloud, GHES) e GitLab (cloud, self-hosted). A camada deile/orchestration/forge/ esconde a diferença sob ForgeClient (ABC); o pipeline nunca chama gh/glab direto.

Cenário DEILE_FORGE_KIND Hosts Tokens
🐙 GitHub cloud (padrão) auto ou github default github.com GITHUB_TOKEN (escopos repo + workflow; sem read:org)
🦊 GitLab cloud gitlab default gitlab.com GITLAB_TOKEN (escopos api, read_repository, write_repository)
🏢 GitHub Enterprise Server github DEILE_GITHUB_HOST=ghe.empresa.com GITHUB_TOKEN do GHES
🏠 GitLab self-hosted gitlab DEILE_GITLAB_HOST=gitlab.empresa.com GITLAB_TOKEN do GL
🌐 Multi-forge na sessão CLI auto ambos declarados AMBOS

🔄 Detecção em 3 camadas (detect_forge_kind)

1. DEILE_FORGE_KIND="github"|"gitlab"?      ✅ override explícito (vence sempre)
2. host da URL bate github.com/gitlab.com   ✅ detecção por URL
   ou DEILE_*_HOST declarado?                  (HTTP probe opt-in via DEILE_FORGE_PROBE=1)
3. project_path com 3+ segmentos → GitLab   ✅ heurística de path
   2 segmentos (owner/repo) → GitHub           (compat retroativa)

GitHubForge opera via gh; GitLabForge via glab + REST v4. O ForgeRouter (singleton) cacheia um cliente por (host, project), permitindo GH e GL na mesma sessão CLI — basta os dois tokens configurados.

🗺️ Vocabulário canônico GitHub ↔ GitLab

Interno GitHub GitLab
Mudança proposta PR MR
Comentário comment note
Reviewer requested_reviewers[] reviewers[]
Numeração pr.number mr.iid
URL /<owner>/<repo>/pull/N /<group>/.../<proj>/-/merge_requests/N
Templates de issue .github/ISSUE_TEMPLATE/*.md .gitlab/issue_templates/*.md
CLI gh glab

⚠️ Pipeline ≠ CLI: o deile-pipeline autônomo é per-repo, per-forge (uma instância serve um repo). Para GH e GL simultâneos em modo autônomo, rode duas instâncias (cada uma com sua DEILE_FORGE_REPO). A sessão CLI interativa não tem essa restrição.

Garantias verificadas: tokens nunca ficam em /proc/self/environ — o wrapper.py move pra ~/.git-credentials (mode 0600) + config do gh/glab e remove de os.environ antes do agente subir; o secrets_scanner detecta tokens GitHub (ghp_/gho_/ghu_/ghs_/ghr_/github_pat_) e GitLab (glpat-/gldt-/glptt-/glsoat-); back-compat 100% com setups GitHub existentes.


☸️ Stack Kubernetes

Quem prefere que o agente não toque o filesystem do host (input não-confiável, sandbox descartável, ou operação autônoma 24/7) sobe a stack em infra/k8s/ — testada em Rancher Desktop (k3s/containerd).

Todos os pods compartilham uma única imagem deile-stack:local (imagePullPolicy: Never); /app é baked no build (COPY, não montado) — mudança de código só vai ao ar após rebuild + restart.

🐳 Os 6 pods

Pod Porta Papel
deilebot :8765 Bridge de I/O (Discord e outros canais) — vive num repo separado
deile-worker :8766 Roda DEILE Python in-process; alvo de dispatch HTTP do pipeline
claude-worker :8767 Roda claude -p em worktrees isolados; auth via CLAUDE_CODE_OAUTH_TOKEN env (Secret claude-credentials, token ~1 ano — issue #603)
deile-pipeline :8768 (status, in-process) O monitor de forge — não recebe dispatch (sem Service de ingestão de tarefas); expõe só o Service read-only deile-pipeline-status p/ o painel. No mais, só "chama pra fora"
deile-monitor :8769 Supervisor determinístico do cluster (vigias V1–V8); distinto do pipeline. Expõe um control-plane on-demand (status sem-LLM, ordens, Q&A read-only via persona monitor_qa — o único pod que vê kubectl + forge + /state) consumido pelo deilebot
deile-shell Sandbox kubectl exec-only, toolset cheio; prompt vem do humano

➕ Frota de CLI workers plugáveis (Decisão #51)

Além dos 6 pods core, qualquer CLI de codificação headless vira um pod-worker despachável. Cada um roda o server genérico cli_worker_server.py (sobre _worker_core.py — lease, heartbeat, subprocess one-shot, gate pós-run de commit+push+test) dirigido por um adapter infra/k8s/cli_adapters/<kind>.py (Protocol CliAdapter). O auto-discovery monta ADAPTERS como fonte única que dirige resolver, painel e geração de manifest/NetworkPolicy — adicionar worker = escrever o adapter, nenhum consumidor é editado (test_worker_registry_drives_everything.py). Todos nascem replicas:0 (scale-to-zero, custo zero ocioso) e são escalados 0→1 sob demanda (cli_worker_scaler.py).

Worker Porta CLI / auth Resume nativo Commit
opencode-worker :8771 opencode · env (OPENROUTER_API_KEY) run --session brief_driven
codex-worker :8772 codex · env (OPENAI_API_KEY); OAuth opt-in (DEILE_CODEX_AUTH=oauth) exec resume brief_driven
qwen-worker :8773 qwen · env (tríade OpenAI-compat) --resume brief_driven
aider-worker :8774 aider · env (OPENROUTER_API_KEY/DEEPSEEK_API_KEY) --restore-chat-history cli_autocommit (--no-attribute-*)
goose-worker :8775 goose · env (OPENROUTER_API_KEY/OPENAI_API_KEY) named-session --resume brief_driven
  • Success gate, não exit-code: WorkResult.ok = adapter.parse_output(...).ok AND o gate pós-run (novo commit + push, ou suíte verde quando o brief exige) — exit-codes de CLI são pouco confiáveis. git_strategy: brief_driven (o brief dirige git add/commit/push) vs cli_autocommit (o aider commita sozinho, sem atribuição).
  • Resume nativo (anti-sangria de custo, #445): cada worker retoma sua sessão nativa no mesmo workdir em vez de re-gastar tokens; o manifest provisiona PVC <kind>-worker-home + CronJob de cleanup. Erro de provider (402/429) é classificado INCOMPLETE para o pipeline retomar.
  • Custo central (#638): o worker devolve um bloco usage no /v1/dispatch; o pipeline (long-lived) grava 1 registro no UsageRepository (SQLite central) por modelo — sobrevive ao scale-to-zero, que o ledger por-PVC não fazia (ledger JSONL fica como fallback local). Tela [T]okens do painel lê o store central primeiro.
  • Antigravity existe só como gate documentado (cli_adapters/antigravity.py não exporta ADAPTER → auto-discovery ignora): closed-source + auth headless inviável; só sai do gate com spike provando Vertex SA.
  • Buildar/instalar: deploy.py k8s build-cli-workers [--kind <k>] (imagem única Dockerfile.cli-worker, multi-stage) → cli-worker-install <kind> (auth por env) ou cli-worker-login <kind> (adapters OAuth-capable, ex.: codex). Provider de workers OpenAI-compat (qwen) via a convenção DEILE_CLI_<KIND>_ENV_<VAR> no .env.

deile-monitor roda um tick em duas fases: Fase A é uma varredura mecânica sem LLM (8 vigias: saúde OAuth, pods em erro, issues órfãs, PRs auto/* com tentativa N/3, aguardando-stakeholder, Jobs falhos, saúde do pipeline, coleta de follow-ups); Fase B só aciona a persona monitor quando candidatos sobrevivem à Fase A. Em regime estável, não gasta token. Tick default a cada 30 min. Tem RBAC dedicado (deile-monitor-sa).

🎛️ Orquestrador: infra/k8s/deploy.py

Imprime um plano antes de qualquer ação mutante; --yes pula o prompt, --dry-run só mostra o plano. Flag global -n <ns> seleciona o namespace (default deile).

Objetivo Comando
Menu interativo / lista de verbos python3 infra/k8s/deploy.py / ... help
Rebuild + restart (deploy de código) python3 infra/k8s/deploy.py k8s build --restart --yes
Provisionar/atualizar a stack (idempotente) python3 infra/k8s/deploy.py k8s up
Criar namespace do zero (interativo) ... k8s create-namespace / ... k8s setup
Escalar workers ... k8s scale --worker 2 --claude-worker 1
Pausar / retomar (scale 0/1; preserva dados) ... k8s stop / ... k8s start
Status / painel TUI / logs ... k8s status / ... k8s panel / ... k8s logs [bot|worker|pipeline|claude-worker]
Bootstrap claude-worker (token ~1 ano) ... k8s claude-setup-token (preferido — issue #603; renovar ~1×/ano re-rodando o verb)
Bootstrap/renovar via OAuth legado (DEPRECATED) ... k8s claude-login [--switch|--no-interactive] / ... k8s claude-renew
Frota CLI: buildar imagem(ns) per-tool ... k8s build-cli-workers [--kind <k>] (via Dockerfile.cli-worker)
Frota CLI: gerar manifest / instalar / login OAuth / remover ... k8s gen-worker <kind> · ... k8s cli-worker-install <kind> · ... k8s cli-worker-login <kind> [--switch|--no-interactive|--in-pod] · ... k8s cli-worker-uninstall <kind>
Clonar repo no deile-shell ... k8s clone <owner/repo>
Listar namespaces DEILE ... k8s list
Teardown (apaga o namespace + dados) ... k8s down

🧱 A imagem

Multi-stage sobre Python 3.11 (slim). Instala, em camadas verificáveis: gh, glab 1.45.0 (SHA256 conferido), kubectl v1.31.4, procps, tini (reaper de zumbis) e o claude CLI pinado em 2.1.158 (via npm). O DEILE Python e os servidores (wrapper.py, worker_server.py, claude_worker_server.py) são copiados como 0555 (read-only em runtime).

🧩 Personalização versionada dos workers (issue #515)

Os pods claude-worker e deile-worker recebem CLAUDE.md/DEILE.md + skills/commands injetados de forma versionada e idempotente, sem instalação em runtime (a NetworkPolicy é default-deny no egress) e sem tocar no código do harness. A fonte canônica vive em infra/k8s/agents/<worker>/; os ConfigMaps claude-worker-agents e deile-worker-agents são derivados dela:

  • claude-worker — initContainer inject-agents (idempotente por cmp -s, set -eu) copia do ConfigMap para o PVC /home/claude/.claude/ (CLAUDE.md, skill brainstorm pinada por commit, command plan.md).
  • deile-worker — ConfigMap montado read-only em /etc/deile/agents/ (skill nativa deile-systematic-debug); worker-settings.json aponta deile_md.user_path/skills_paths para lá.

🛡️ Defesas aplicadas em todos os pods

runAsNonRoot uid 10001 · capabilities drop ALL · readOnlyRootFilesystem · allowPrivilegeEscalation: false · seccompProfile RuntimeDefault · automountServiceAccountToken: false (exceto os pods que precisam renovar OAuth via kubectl exec) · PSS restricted no namespace · NetworkPolicy default-deny-all + opt-ins explícitos por porta · secrets montados como arquivos em /run/secrets/<role>/ (nunca via env:, então /proc/<pid>/environ fica limpo) · bootstrap_providers() popa as API keys de os.environ após instanciar os providers.

🚫 Honestidade: a "whitelist de egress" do claude-worker (api.anthropic.com, github.com, gitlab.com, granularidade de repo) é enforçada na aplicação (wrapper.py + ConfigMap claude-worker-allowed-repos, fail-closed), não em L3/L4 — a NetworkPolicy libera TCP 443 genérico. É um controle de aplicação, não firewall de rede. A allowlist de repos agora é reverificada por request, antes de qualquer clone, nos dois servidores de dispatch (_worker_core.check_repo_allowed → 403 REPO_NOT_ALLOWED), fechando o gap onde só havia fail-fast no startup (issue #639).

🌐 Multi-namespace

O DEILE roda múltiplas stacks lado a lado, uma por namespace (deile é a default; deile-gl é o piloto GitLab). Os 6 deployments core não fixam namespace — o -n <ns> do deploy.py os aplica em qualquer namespace. Gap conhecido: alguns manifests auxiliares (NetworkPolicy, claude-worker, certos PVCs/CronJobs) ainda fixam namespace: deile — multi-namespace é pleno para o core, parcial para os auxiliares.

🖥️ Painel TUI & auditoria de custo

deploy.py k8s panel abre um cockpit Rich navegável (não fecha após a escolha): pods (k8s + processos locais), timeline do pipeline, backlog de issues/PRs, feed de atividade e sessões claude -p ao vivo (parser incremental de JSONL). Hotkeys: [1-4] views · [t] auditoria de tokens (suspende e roda session_tokens_audit.py) · [M] monitor · [d] matriz de dispatch (editar por etapa: Worker × Model × Timeout × Retries × Cost-cap × Reasoning; [L] login claude, [I] install, [s] scale, [c] cleanup, [p] max_parallel, [J] retenção JSONL) · [?] ajuda · [q] sair.


🔭 Observabilidade e eventos

📨 Event bus

deile/events/event_bus.pyEventBus assíncrono com enum EventType: sistema (SYSTEM_STARTED/STOPPED), persona (PERSONA_ACTIVATED/...), tarefas (TASK_CREATED/STARTED/COMPLETED/FAILED/CANCELLED), código (CODE_GENERATED/EXECUTED/TESTED, FILE_MODIFIED) e ferramentas (TOOL_INVOKED/COMPLETED/FAILED). Handlers em deile/events/event_handlers.py.

📈 OpenTelemetry (deile/observability/)

Tracer + métricas CNCF, com fallback no-op quando o SDK está ausente, DEILE_OTLP_ENDPOINT vazio ou DEILE_OBSERVABILITY_DISABLED=true. Toda chamada é best-effort — nunca quebra o turno.

  • Spans: deile.turn (1 por interação), deile.tool.<name> (1 por execução), deile.llm.call (1 por chamada de provider). Adapter de dispatch: root span deile.dispatch + eventos dispatch.* e child spans git.commit / git.push / forge.pr_open / forge.pr_review. Propagação W3C traceparent pipeline→worker (issue #457): o deile.dispatch vira filho do span pipeline.dispatch_request; os child spans git.*/forge.* dual-emitem atributos SemConv vcs.* (vcs.ref.head.name, vcs.repository.url, vcs.change.id/state — issue #456, toggle DEILE_OTLP_SEMCONV_ENABLED, default on).
  • Métricas: do turno/tool — deile.tokens.total, deile.cost.usd.total, deile.tool.duration_ms, deile.turn.duration_ms, deile.errors.total; do dispatch autônomo (issue #455, dispatch_metrics.py)deile.dispatch.total / .failed.total / .duration_ms / .tool_burst.total, deile.forge.pr_review.total, deile.git.push.total (todas com labels de cardinalidade limitada — enum fechado, nunca task_id/session_id/sha/model).
  • Sem segredos em atributos — apenas tamanhos, tokens, custo e IDs opacos (redação automática de tokens). session_id deliberadamente não é label (controle de cardinalidade).
  • Env: DEILE_OTLP_ENDPOINT (vazio = off), DEILE_OTLP_HEADERS, DEILE_OTLP_INSECURE, DEILE_OTLP_SERVICE_NAME, DEILE_OTLP_SAMPLE_RATIO, DEILE_OTLP_SEMCONV_ENABLED, DEILE_OBSERVABILITY_DISABLED.

🩺 Runtime state por processo (deile/runtime/)

Cada processo DEILE publica seu estado vivo em ~/.deile/run/<instance_id>.json (escrita atômica + cleanup no atexit), com heartbeat a cada 2 s. O current_action é um enum (idle/starting/tool_execution/llm_call/shutting_down) e o state file acumula tokens/custo/turns/tool_calls/errors — sem segredos, prompts ou tool_args. Um status server por Unix-socket (<id>.sock, chmod 0600) responde STATUS/METRICS/FLUSH em protocolo de linha; um registry.json (lock fcntl.flock, GC de PIDs mortos) dá visão de frota. No Windows, vira no-op.

🔌 Endpoints de control-plane (internos ao cluster)

Não há servidor HTTP público — o agente interativo é puro CLI. Estes endpoints são o control-plane interno do cluster, protegidos por Bearer (exceto /health e o fluxo OAuth).

deile-worker (:8766)GET /v1/health · POST /v1/dispatch · GET /v1/result/{task_id} · GET /v1/progress/{task_id}

claude-worker (:8767)GET /v1/health · GET /v1/auth/start · GET /v1/auth/status · GET /v1/pod-status · POST /v1/dispatch · GET /v1/progress/{task_id} · GET /v1/dispatches/{task_id}/resume-info · GET /v1/sessions · GET /v1/sessions/{id}/{command,chat,stdout} · POST /v1/sessions/{id}/kill · DELETE /v1/sessions/{id}/cleanup · GET\|POST /v1/cleanup

Frota de CLI workers (cli_worker_server.py, portas derivadas do adapter) — GET /v1/health · GET /v1/models · POST /v1/dispatch · GET /v1/progress/{task_id} · GET /v1/dispatches/{task_id}/resume-info

deile-pipeline status (:8768)GET /v1/health · GET /v1/pipeline-status[/backlog\|/recent\|/ledger\|/reaper-preview] · POST /v1/pipeline/force-tick

deile-monitor (:8769)GET /v1/health · GET /v1/monitor-status · POST /v1/command · POST /v1/ask · GET /v1/ask/{request_id}


🔒 Segurança e auditoria

Componente Onde Papel
🛡️ Permissões deile/security/permissions.py (PermissionManager) Verifica permissão antes de ação privilegiada
📜 Audit log deile/security/audit_logger.py (AuditLogger + AuditEvent tipado) Registro tipado de ações sensíveis
🔍 Scanner de segredos deile/security/secrets_scanner.py Detecta/redige credenciais (tokens GitHub e GitLab, entre outros)
✅ Aprovação por risco deile/orchestration/approval_system.py (ApprovalSystem) Gate de ações de alto risco (ex.: DM, menção de cargo)

Toda tool declara um SecurityLevel (deile/tools/base.py); o bash_tool tem catálogo de risco próprio (assess_risk em deile/tools/_shell_security.py, classificando safe/moderate/dangerous sobre o mesmo enum SecurityLevel). O gate de aprovação interativa das tools de mensageria (DM / menção de cargo) pode ser auto-dispensado para um operador confiável via approval.auto: true em ~/.deile/settings.json (bot_approval_auto).


💾 Persistência

Os stores relacionais são SQLite auto-criados em runtime (o ledger de custo é JSONL append-only) — não há script SQL versionado;

Store Arquivo Dono
Tarefas & listas ./.deile/db/tasks.db (legacy: ./deile_tasks.db) deile/orchestration/sqlite_task_manager.py
Telemetria de uso/custo ~/.deile/db/usage.db deile/storage/usage_repository.py (inclui o custo da frota CLI empurrado centralmente — issue #638)
Memória episódica episodes.db deile/memory/episodic_memory.py (aiosqlite)
Cron genérico data/cron.db deile/cron/store.py (CronStore)
Ledger de custo durável (claude-worker) ~/.claude/cost-ledger.jsonl claude-worker (JSONL append-only, dedup por session_id)
Ledger de custo durável (frota CLI) <root>/.cost-ledger.jsonl por PVC cli_worker_server (fallback local; dedup por task_id)
.deile/db/tasks.db          ── task_lists ──1:N── tasks   (legacy: ./deile_tasks.db)
~/.deile/db/usage.db        ── usage_records  (tokens, custo USD, provider/model)
episodes.db                 ── episódios da sessão
data/cron.db                ── cron entries  (CronStore + CronRunner)
~/.claude/cost-ledger.jsonl ── custo por sessão claude -p (JSONL; colhido antes de podar o transcript)

💰 Ledger de custo (issue #445): os transcripts do claude -p acoplam continuidade --resume (volumoso, efêmero) e auditoria de custo (minúsculo, permanente). No cleanup, o custo de cada sessão órfã é colhido para o ledger durável antes de o transcript ser podado — custo histórico permanente em escala de KB, transcripts podam livremente. A ferramenta infra/k8s/session_tokens_audit.py lê o ledger (sessões podadas) + o JSONL vivo (recentes) com custo idêntico (mesma tabela de preços em jsonl_cost.py).

💰 Custo central da frota CLI (issue #638): o custo dos workers da frota multi-CLI não depende mais só do ledger por-PVC (que sumia no scale-to-zero ou force-delete). O pipeline (componente longevo) lê o bloco usage estruturado da resposta do /v1/dispatch e grava 1 registro por modelo no UsageRepository central via fleet_cost_recorder (caminho wait direto, fire-and-forget capturado no reconcile via resume-info, dedup por task_id). Preço pela fonte única jsonl_cost.fleet_cost_of_model; escrita best-effort — falha nunca derruba o dispatch. A tela [T]okens do painel lê o store central como fonte primária.


🏗️ Arquitetura e camadas

Arquitetura hexagonal por camadas, com registries para artefatos extensíveis (tools, commands, parsers, personas, skills) — adicionar um artefato não exige tocar no núcleo (Open/Closed). I/O é async-first.

Camada Pacote Responsabilidade
🧩 Núcleo deile/core/ Lógica central, integração com modelos, tool-loop
🤖 Modelos LLM deile/core/models/ Provedores, roteamento, streaming, reasoning
📨 Eventos deile/events/ Event bus assíncrono e handlers
🛠️ Ferramentas deile/tools/ Registry e implementação de tools (+ mensageria)
📜 Comandos deile/commands/ Slash commands e despacho
🧱 Parsers deile/parsers/ Parsing de entrada (arquivos, diffs, refs, comandos)
🎭 Personas deile/personas/ Instruções MD/YAML e manager
🧩 Skills deile/skills/ Discovery, registry, hot-reload
🧠 Memória deile/memory/ Quatro camadas
🔒 Segurança deile/security/ Permissões, audit, secrets scanner
💾 Armazenamento deile/storage/ Logger, usage/custo, budget guard, embeddings
🎯 Orquestração deile/orchestration/ Planos, workflows, tarefas, aprovações, pipeline, forge
🩺 Runtime deile/runtime/ State file por processo, status server, registry de frota
🔭 Observabilidade deile/observability/ Tracer/métricas OTLP, adapter de dispatch
⏰ Cron deile/cron/ CronStore (SQLite) + CronRunner
🖥️ UI deile/ui/ Renderização, streaming, painel, sub-agent panel
🧬 Evolução deile/evolution/ Auto-learning experimental
🔌 Plugins deile/plugins/ Plugin manager, hot-reload (sem sandbox — ver Limitações)
⚙️ Infra deile/infrastructure/ Adapters externos (SDKs, drivers)
🛠️ Configuração deile/config/ Settings singleton, YAML, profiles
🔗 Integrações deile/integrations/ Cliente HTTP do control-plane (flecha reversa agente → bot)
🪵 Log mgmt deile/log_mgmt/ Análise, rotação e dispatch de logs
🧰 Preferências deile/preferences/ Backing store das tools remember/list/forget_preference

Fluxo de uma mensagem do usuário:

  1. _DeileCLI (deile/cli.py) lê a entrada e encaminha ao DeileAgentdeile.py é só o launcher que prepara o venv e delega a deile.cli.main().
  2. Parsers extraem menções a arquivos/comandos.
  3. Slash command → despacha via CommandRegistry; senão segue para o modelo.
  4. ModelRouter/TierRouter escolhe provider/modelo conforme tier e estratégia.
  5. O provider emite UnifiedStreamEvent (TEXT_DELTA, TOOL_USE_START/TOOL_USE_END, …).
  6. Num evento de tool use, o ToolLoopExecutor executa a tool e devolve o resultado à conversa (até max_tool_iterations, default 100, ajustável via DEILE_MAX_TOOL_ITERATIONS).
  7. O StreamingRenderer acompanha eventos e atualiza o terminal ao vivo (rich.live.Live).
  8. O EventBus publica eventos (telemetria, persona, tool).

⚙️ Configuração

🔑 Variáveis de ambiente

A referência canônica é o .env.example (~540 linhas, seções comentadas): chaves de LLM, forges, bot, workers, pipeline (dispatch/model/reasoning por etapa), subagents, cron, OpenTelemetry, status server, etc. A leitura de config deve passar por get_settings() (deile/config/settings.py) — é um princípio arquitetural; alguns módulos do pipeline ainda leem os.environ direto (gap conhecido).

🧩 Injeção de env por worker da frota: a convenção DEILE_CLI_<KIND>_ENV_<VARNAME>=<valor> no .env injeta <VARNAME> no Deployment do worker <kind> ao renderizar o manifest. Sensíveis (terminam em _API_KEY/_TOKEN ou casam auth_env_keys) viram secretKeyRef no Secret cli-worker-keys; não-sensíveis (ex.: OPENAI_BASE_URL, OPENAI_MODEL) entram como literal. É assim que se aponta um worker OpenAI-compat (qwen) para Dashscope/OpenRouter/OpenAI. OAuth opt-in por worker via DEILE_<KIND>_AUTH=oauth.

🗂️ Settings em camadas

~/.deile/settings.json é resolvido em três camadas com precedência project > user > profile: profile (preset) → user (~/.deile/settings.json) → project (<cwd>/.deile/settings.json, com opt-in via trust.project_layer_dirs). Ajuste por /settings set <chave> <valor> no CLI.

📄 Arquivos de configuração

  • deile/config/ (código + YAML): model_providers.yaml, intent_patterns.yaml, persona_config.yaml, commands.yaml, skills.yaml, system_config.yaml, api_config.yaml + profiles/.
  • config/ (raiz, runtime): settings.json, deilebot.yaml, pipeline_schedule_*.yaml, persona_config.yaml.

dois diretórios config/ (raiz e deile/). Não confundir.


📋 Requisitos do sistema

🐍 Linguagem e plataforma

  • Python ≥ 3.9 · Linux/macOS (Windows experimental) · entrada python3 deile.py.

📦 Dependências de produção (requirements.txt)

  • 🤖 LLM SDKs: anthropic, openai, google-genai
  • 🖥️ UI/CLI: rich, prompt_toolkit, colorama, Pygments
  • ⚡ Async I/O: aiofiles, aiosqlite
  • ✅ Validação/config: pydantic, pydantic-settings, PyYAML, python-dotenv
  • 🌐 Rede/sistema: requests, httplib2, psutil, chardet, GitPython, tenacity
  • 📚 Outras: numpy, pathspec, watchdog

🧩 Extras opcionais (pyproject.toml)

Extra Para quê
bot deilebot (git URL — repo separado elimarcavalli/deilebot)
otel OpenTelemetry (api/sdk/exporter OTLP gRPC)
ui textual
scheduler · webhook · test APScheduler · FastAPI/uvicorn · pytest & cia.

🧪 Dependências de desenvolvimento (dev-requirements.txt)

Testes (pytest, pytest-asyncio, pytest-mock, pytest-cov, pytest-xdist, pytest-benchmark), qualidade (coverage, isort, radon, black) e segurança (safety, bandit). (o pytest-timeout usado pelo pytest.ini vem do extra [test] do pyproject.toml.)


🧪 Testes

Configuração em pytest.ini:

  • testpaths = deile/tests; coleta test_*.py e *_test.py.
  • asyncio_mode = auto (testes async dispensam @pytest.mark.asyncio).
  • --strict-markers + --strict-config — markers novos precisam ser registrados antes de usar.
  • Timeout de 300 s por teste (pytest-timeout, modo thread).
  • Markers registrados: unit, integration, security, orchestration, bash, ui, slow, perf, e2e, e2e_discord_live, e2e_telegram_live, e2e_whatsapp_live, e2e_meta_live, manual, llm.
python3 -m pytest deile/tests/ -q          # suíte completa (resumo)
python3 -m pytest deile/tests/path/test_x.py -v   # um arquivo

ℹ️ O deile/tests/ mistura pytest tests (test_*.py, coletados) e scripts standalone (*_test.py, smoke_test_*.py) rodados manualmente. Testes que consomem token real ficam em deile/tests/might/ (opt-in, fora da suíte padrão). O pytest.ini não tem --cov-fail-under — o gate de cobertura é aplicado só no CI.

🚦 Gates de CI (.github/workflows/ci.yml)

O CI virou gate real (hardening em 3 etapas) — todas as Actions são SHA-pinadas e cada job tem permissions: contents: read:

Etapa 1/3 — segurança & supply-chain (#732):

  • test — roda a suíte real deile/tests/ paralela (pytest-xdist -n auto) com --cov-fail-under=85 (cobertura medida: 87%). Antes apontava para tests/ (inexistente) e mascarava o exit code — CI verde era teatro (corrigido em #724).
  • secret-scangitleaks (full-history) com allowlist de FPs em .gitleaks.toml (restrita aos arquivos de teste que usam segredos fake).
  • security-scanbandit -lll (só HIGH, zero achados legados) + pip-audit sem mascarar.

Etapa 2/3 — build, artefato & smoke (#733):

  • functional-tests (advisory → gating) — builda o wheel, instala em venv limpo e valida deile --version/--help (exit 0) + import dos registries core.
  • build-and-package (advisory → gating) — wheel + sdist, twine check, smoke de install-from-wheel, todos os extras resolvidos offline ([test], [otel], [scheduler], [webhook], [ui]) em venvs isolados, Docker build da imagem deile-stack:local (buildx + cache GHA) e smoke de import dos módulos críticos dos pods. performance-tests (que coletava 0 benchmarks — teatro) foi removido.
  • deployment-ready passa a exigir secret-scan, security-scan, functional-tests, build-and-package, code-quality e documentation.

Etapa 3/3 — qualidade de código (#736):

  • code-quality (advisory → gating) — dois gates sem reformatação (formatação/mypy ficam para a issue #735 de pós-reformat): interrogate deile/ --fail-under=39 (cobertura de docstrings ≥ 39%; baseline 39,9% em 2026-06-16; ratchet: só pode aumentar) + radon cc deile/ -a com falha se a complexidade ciclomática média ≥ 10,0 (nota B→C; baseline A/3,24 em 2026-06-16). mypy corre em modo advisory (continue-on-error: true) até o gate real no #735.
  • documentation (sempre presente) — scripts/validate_doc_consistency.py verifica invariantes doc↔código (--cov-fail-under no ci.yml e não no pytest.ini, cross-refs de docs/system_design/ para arquivos existentes, presença do gate de testes); pymarkdown corre em modo advisory (~40 violações legadas, gate real após limpeza sistemática).

🚦 Operação e troubleshooting

Sintoma Causa provável / ação
bootstrap_providers não acha providers Nenhuma API key definida — edite o .env
Erro --strict-markers no pytest Marker novo não registrado — registre em pytest.ini
Tool "não encontrada" Garanta que está em DEFAULT_TOOL_PACKAGES ou registrada via register_tool
Mudança de código no cluster sem efeito /app é baked — rode deploy.py k8s build --restart
Pod em erro de auth / WORKER_AUTH_EXPIRED Token do claude-worker expirou/ausente — re-rode deploy.py k8s claude-setup-token (token de ~1 ano; claude-renew só se ainda em OAuth legado)
Erro de banco durante uma tarefa O agente para e reporta — scripts SQL -> humano

💪 Pontos fortes / diferenciais técnicos

  • 🔁 Tool-loop único e provider-agnóstico — elimina duplicação por provider.
  • 🌐 Fallback automático entre 4 provedores com circuit breaker por provider.
  • 🧭 Forge-agnóstico — o mesmo pipeline opera GitHub e GitLab idênticos.
  • 🤖 Loop autônomo DEILE-a-DEILE OU DEILE-a-Claude — escolha o worker por etapa.
  • 🧠 Portão de refinamento — critica o escopo da issue antes de escrever código.
  • 💵 Telemetria de custo persistente + ledger durável que sobrevive à poda de transcripts.
  • 🔭 Observabilidade enterprise (OTLP) + runtime state por processo + painel TUI.
  • 🎭 Personas e skills MD-driven — editáveis sem tocar no Python; hot-reload.
  • 🔒 Auditoria tipada + scanner de segredos + aprovação por risco nativos.
  • 🐳 Hardening de container — non-root, drop ALL caps, RO rootfs, secrets como arquivos, NetworkPolicy default-deny.

⚠️ Limitações conhecidas

  • O agente interativo não expõe servidor HTTP público — é puro CLI. Os endpoints HTTP existem só como control-plane interno do cluster (workers/pipeline-status, com Bearer).
  • IDs de modelo no YAML são literais — precisam ser válidos no SDK do provider.
  • Tool-loop tem teto (max_tool_iterations, default 100, configurável).
  • Módulo de evolução é experimental.
  • Plugins não têm sandbox real — carregue apenas plugins auditados.
  • /sandbox é um toggle informativo — não fornece isolamento (use a stack K8s para isolamento de verdade).
  • Multi-namespace é pleno para os 6 deployments core, parcial para manifests auxiliares (alguns ainda fixam namespace: deile).
  • A whitelist de egress do claude-worker é controle de aplicação (wrapper.py), não de rede (L3/L4) — gap documentado; FU prioritária é um sidecar de proxy de credenciais.

🤝 Como contribuir

Contribuições são bem-vindas — corrija typos, crie tools, comandos, parsers, personas, skills ou providers.

⚡ Fluxo recomendado com gh

# Etapa Comandos gh principais
1 Explorar issues gh issue list · gh issue view <id>
2 Criar issue (siga o template apropriado) gh issue create --title "..." --body "..."
3 Branch vinculada gh issue develop <id> --checkout
4 Implementar + testar python3 -m pytest deile/tests/ -q · ruff check deile/ · isort --check-only deile/
5 Commitar (Conventional Commits) git add -p · git commit -m "feat(scope): ..."
6 Abrir PR gh pr create --fill
7 Revisar / merge gh pr review <id> --approve · gh pr merge <id> --squash

⚠️ Edições de label devem usar o endpoint REST (gh api .../labels), não gh issue edit --add-label — este último dispara uma query GraphQL que exige o escopo read:org (que o token do pipeline não tem).

🧩 Onde adicionar extensões

O quê Onde Observação
🛠️ Tool deile/tools/<nome>.py Adicione ao DEFAULT_TOOL_PACKAGES ou registre via register_tool
⌨️ Slash command deile/commands/builtin/<nome>.py Registre no CommandRegistry; ganha flag CLI automática
🗂️ Parser deile/parsers/<nome>.py Siga o contrato base + priority
🎭 Persona personas/instructions/ + personas/library/ MD/YAML, sem Python
🧩 Skill ~/.deile/skills/, .deile/skills/ ou deile/skills/library/ MD com frontmatter; hot-reload
🧠 Provider deile/core/models/ Registre em bootstrap.py + YAML dos modelos

✅ Checklist antes do PR

✔️ Item
[ ] Suíte completa verde (python3 -m pytest deile/tests/ -q)
[ ] ruff check deile/ e isort --check-only deile/ sem pendências
[ ] Commits no padrão Conventional Commits
[ ] README/docs atualizados se necessário
[ ] Nenhum arquivo sensível ou residual commitado

Para detalhes de arquitetura, abra a base de conhecimento em docs/system_design/00-VISAO-GERAL.md (índice dos 14 pilares + registro de decisões).


📄 Licença

Projeto licenciado sob MIT License.

👤 Construtores

Nome GitHub Site E-mail
Elimar Cavalli @elimarcavalli elimar.dev elimar.dev@gmail.com
DEILE-One @DEILE-One elimarcavalli/deile deile-one@elimar.dev

DEILEpython3 deile.py

About

DEILE A.I AGENT CLI & K8s Harness Multi-CLI

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages