# Octopus — Manual Completo

> Agente pessoal de IA do Robson, rodando em produção na VPS RSTECBR.
> Atualizado em 2026-07-12. Fonte da verdade: o código em `/root/Octopus`.

---

## 1. O que é

Octopus é um agente de IA próprio — não é um wrapper de chat. Ele conversa (Web/PWA e Telegram),
raciocina em loop com ferramentas (ReAct), executa habilidades (skills), lê e escreve arquivos,
pesquisa na web, gera imagens, guarda memória de longo prazo, trabalha dentro de projetos com
regras e conhecimento próprios, e propõe conhecimento novo a partir do que executa — **sujeito à sua
aprovação** (2026-07-12: antes ele criava skills sozinho e as ativava na hora; o resultado medido
foram 11 skills auto-geradas redundantes, então o aprendizado passou a ter crivo).

A metáfora dá nome às partes: o **cérebro** (o loop de raciocínio) e os **tentáculos** (skills
delegadas que rodam com contexto isolado).

| | |
|---|---|
| Onde roda | VPS RSTECBR, `/root/Octopus`, serviço systemd `octopus.service` |
| Interface Web | `https://painel.rstecbr.com.br` (PWA, porta interna 3030) |
| Interface Chat | Telegram (bot privado) |
| Banco | PostgreSQL na porta 5435 |
| Objetos | MinIO (buckets de arquivos) |
| Stack | TypeScript + Node, sem framework web (HTTP nativo) |
| Skills instaladas | 17 (eram 38 — a limpeza de 2026-07-12 tirou o que era redundante ou pressupunha shell) |
| Ferramentas | 27, entregues à API no campo nativo `tools` |
| Testes | 254 unitários + 16 self-tests de tela (Playwright, contra a VPS de verdade) |

---

## 2. Arquitetura

```
                     ┌───────────────┐   ┌──────────────┐
   Telegram ─────────►               │   │              │
                     │ AgentController├──►│  AgentLoop   │  (ReAct: pensa → usa ferramenta → observa)
   PWA / Web ────────►               │   │              │
     (/api/chat)     └───────┬───────┘   └──────┬───────┘
                             │                  │
        ┌────────────────────┼──────────────────┼────────────────────┐
        │                    │                  │                    │
  ┌─────▼──────┐      ┌──────▼──────┐    ┌──────▼──────┐      ┌──────▼──────┐
  │ MemoryMgr  │      │ SkillRouter │    │ToolRegistry │      │ProviderFactory│
  │ (Postgres) │      │  + Executor │    │ (24 tools)  │      │  → Router     │
  └────────────┘      └─────────────┘    └─────────────┘      └──────┬────────┘
                                                                      │
                                              ┌───────────────────────▼──────────────────┐
                                              │ FallbackChainProvider (cadeia por tarefa)│
                                              │ Anthropic│Gemini│Groq│OpenRouter│locais  │
                                              └──────────────────────────────────────────┘
```

**Turno de conversa** (`AgentController.runPipeline`) — núcleo único, compartilhado por Telegram e Web:

1. Carrega a conversa e o histórico (e o projeto ativo, se houver).
2. Resolve o **modelo** (precedência: conversa → skill → função → automático).
3. Roteia para uma **skill** (barra `/nome` ou classificação semântica).
4. Monta o **system prompt**: persona → instrução do projeto → regras → catálogo de skills → `CACHE_BREAKPOINT` → fatos → contexto do dispositivo.
5. **Escopa as ferramentas do turno**: skill que declara `allowed_tools` recebe só as dela (o esquema
   das 27 custa 4.737 tokens; o do `reversa`, 1.125 — 76% menos). Chat sem skill vê todas, de propósito.
6. Roda o **AgentLoop** com **tool calling nativo** (as ferramentas vão no campo `tools` da API, não
   escritas em prosa no prompt) e **compactação do contexto** da tarefa.
7. **Confere a pós-condição**: se a resposta afirma ter criado um arquivo, o arquivo é procurado no
   disco antes de a resposta sair.
8. Salva as mensagens, gera título, resume memória longa, e o **LearningLoop** propõe conhecimento —
   que fica **em espera** até você aprovar.
7. Registra a telemetria do turno (**tempo de resposta + modelo que respondeu**), exibida no rodapé de cada resposta no chat.

Tudo antes do `CACHE_BREAKPOINT` é prefixo estável → **prompt caching** da Anthropic. É o que segura o custo.

---

## 3. Modelos e provedores

### 3.1 Catálogo (`data/providers.json`)

Provedores: `openrouter` (por onde passa quase tudo hoje), `groq`, `llama-local`, `llama-small-local`.
Os dois últimos são servidores llama.cpp na própria VPS — custo zero, e é só isso que têm de bom (ver
abaixo). As contas diretas de Anthropic/OpenAI/Gemini/DeepSeek secaram e estão marcadas `disabled` no
catálogo: continuam registradas, com preço e quota, pra voltarem numa linha se o crédito voltar.

**Modelos locais (2026-07-12):**

| Serviço | Modelo | Porta | Serve pra quê |
|---|---|---|---|
| `llama-main` | Meta-Llama-3.1-8B-Instruct **Q4_K_M** | 7070 | resumo, título, memória longa |
| `llama-small` | Llama-3.2-3B-Instruct **Q4_K_M** | 8881 | classificação, roteamento |
| `llama-embed` | embeddinggemma-300m (768 dims) | 7071 | **embeddings** — roteamento de skills, fatos, biblioteca |

Substituíram o Gemma 4 12B e o Qwen 2.5 Coder 1.5B. O Gemma levava ~27s pra dizer "ok" e não tinha tool
calling; o Qwen era um modelo de **código** fazendo trabalho de classificação.

> **Modelo local não serve pra chat, e isto é medido, não achismo.** O 8B responde "ok" em 5s — mas com
> 2.571 tokens de prompt leva **93 segundos**, e com o prompt real do agente (~10k tokens, incluindo o
> esquema das ferramentas) levou **152 segundos** e respondeu mal. Ele serve pro que não tem gente
> esperando: resumo, título, classificação, tarefa de fundo. Por isso ele **saiu do fim das cadeias de
> chat** — um fallback de 3 minutos não é fallback, é uma parede.

### 3.2 Cadeias de fallback (classes de tarefa)

Se um elo falha (erro, sem crédito, rate limit, contexto estourado), o próximo assume. Sem intervenção.

| Classe | Cadeia |
|---|---|
| `heavy-reasoning` | claude-sonnet-5 → deepseek-v4-pro → gemini-3.1-pro → gpt-oss-120b (Groq) → nemotron-3-ultra (free) → **deepseek-v4-flash** |
| `code` | claude-sonnet-5 → deepseek-v4-pro → gemini-3.1-pro → gpt-oss-120b → **deepseek-v4-flash** |
| `light-chat` | deepseek-v4-flash → gemini-3.1-flash-lite → llama-3.3-70b |
| `communication` | claude-haiku-4.5 → deepseek-v4-flash → llama-3.3-70b |
| `classification` | **Llama 3.2 3B local** → llama-3.3-70b → gemini-3.1-flash-lite |
| `summarization` | **Llama 3.1 8B local** → deepseek-v4-flash |
| `vision` | gemini-3-flash → claude-haiku-4.5 |

O fim das cadeias com humano esperando é o `deepseek-v4-flash` (US$0,08/Mtok): custa quase nada e
responde em segundos. Onde ninguém espera (classificação, resumo), o local vem primeiro — e de graça.

### 3.3 Comparação real de modelos (2026-07-12)

Mesma tarefa pra todos, exigindo **tool calling** de verdade ("quantos arquivos `.ts` existem em
`src/services`? use suas ferramentas"), cada um fixado na sua conversa. Custo e cache vêm do livro-caixa.

| Modelo | Tempo | Custo | Acertou? | Observação |
|---|---|---|---|---|
| **claude-haiku-4.5** | **3,1s** | US$0,0124 | ✅ | o mais rápido dos que acertaram |
| gemini-3.1-flash-lite | 4,4s | US$0,0032 | ✅ | ótimo custo/tempo |
| claude-sonnet-5 | 9,3s | US$0,0288 | ❌ (disse 18) | errou por 1 nesta rodada |
| **deepseek-v4-flash** | 10,1s | **US$0,0014** | ✅ | **21× mais barato que o Sonnet** |
| deepseek-v4-pro | 10,2s | US$0,0074 | ✅ | |
| gemini-3.1-pro | 15,4s | US$0,0370 | ✅ | o mais caro |
| gpt-oss-120b (Groq) | 11,7s | — | — | **não respondeu**: free tier tem 8k TPM e o prompt do agente tem ~12k |
| llama-3.3-70b (Groq) | 11,6s | — | — | mesmo problema |
| nemotron-3-ultra `:free` | 30,9s | US$0 | ❌ (disse 2) | lento **e** errado |
| **Llama 3.1 8B local** | **300s+** | US$0 | ❌ | não terminou |

O que isso mudou nas cadeias, na hora:

- **Nemotron free e os modelos do Groq saíram das cadeias de chat.** O Groq continua na
  `classification`, onde o prompt é pequeno e cabe no free tier. Um elo que não consegue receber o
  prompt não é fallback — é atraso garantido antes do próximo elo.
- **O local saiu do fim das cadeias de chat** (ver acima).
- Sobrou uma fila que só tem modelo que responde: pago bom → pago barato.

> Nota honesta: é **uma** rodada, não um veredito de qualidade. O Sonnet errar por 1 aqui não o
> desqualifica; o que a tabela decide bem é o que **não funciona** (Groq no chat, Nemotron, local).

### 3.4 Sondagem de disponibilidade

Ter chave ≠ estar disponível. No boot, o `ModelHealthService` faz **uma chamada real** a cada modelo do
catálogo e grava `data/model_health.json`. **Só o que responde aparece nos seletores.** Os indisponíveis
ficam listados nas Configurações com o motivo ("sem crédito/quota na conta", "modelo não existe mais
nesta conta", "servidor fora do ar"). Botão **🔍 Re-sondar disponibilidade** refaz sob demanda.

Estado atual: **12 disponíveis**, 3 fora (OpenAI e DeepSeek — chave válida, conta sem crédito).

### 3.5 Seleção de modelo (precedência)

```
conversa (seletor no topo do chat)  >  skill (Configurações → Modelos)
        >  função (chat / voz — padrão da voz: Haiku)  >  automático (cadeia + disjuntor)
```

Modelo fixado sai da cadeia — quem escolhe assume o controle. Dois anteparos protegem esse caso:

- **ContextSafeProvider**: se o prompt estoura a janela do modelo, poda e re-tenta em vez de quebrar.
- **PinnedWithFallbackProvider** (2026-07-12): se o modelo fixado **falha**, a conversa cai na cadeia
  automática em vez de morrer junto — com o motivo no log de atividade. Veio de um caso real: o chat
  ficou fixado num modelo de 8k de janela, o prompt do agente tem ~10k, e TODA mensagem morria. O
  sintoma que chegou ("a caixa do chat está bloqueada") não tinha nada a ver com a caixa. Preferência
  não é bilhete suicida.

No seletor, o modelo **pago mostra o preço** (`💲 claude-sonnet-5 · US$2/10 Mtok`). Antes o "pago" vinha
do provedor — e como o OpenRouter serve Sonnet pago e modelos `:free` pela mesma chave, o Sonnet
aparecia como grátis.

### 3.6 Controle de gasto

- **SpendCircuitBreaker** (`data/circuit_breaker.json`): teto mensal, hoje **R$ 55**. Estourou → só elos gratuitos/locais.
- **QuotaTracker**: contabiliza requisições por free tier.
- **UsageLedger** (2026-07-12, tabela `llm_usage`): o **livro-caixa**. Uma linha por chamada — modelo,
  classe de tarefa, tokens (inclusive os de cache), custo, duração — etiquetada com conversa, projeto e
  skill. O disjuntor diz quanto falta pro teto; o livro-caixa diz **pra onde o dinheiro foi**. Visível em
  Configurações → Geral → Custo, e em `GET /api/v1/usage?days=30`.
- Modelo fixado manualmente **não** passa pelo disjuntor (é escolha consciente do dono), mas o gasto continua sendo somado.

### 3.7 Prompt caching (o maior corte de custo até hoje)

O livro-caixa, na primeira medição, mostrou **12.701 tokens de entrada e zero de cache** num turno que só
listou um diretório — US$0,024 por uma pergunta trivial. O `cache_control` só era montado no formato
Anthropic, mas **todas as cadeias passam pelo OpenRouter**, que fala formato OpenAI: o caching existia e
nunca rodava no caminho real. Era o tipo de coisa que só aparece quando se mede.

Hoje o marcador vai nas partes de conteúdo do esquema OpenAI, e o prefixo cacheado inclui o esquema das
ferramentas (na Anthropic, `tools` vem antes do `system` na ordem de cache).

**Medido:** 176 tokens novos + 11.995 lidos do cache → **US$0,0244 → US$0,0028 por turno (8,7× mais barato).**

---

## 4. Skills (tentáculos)

Uma skill é uma pasta em `.agents/skills/<id>/SKILL.md` com frontmatter YAML:

```yaml
---
name: landing-page-especialista
description: Cria landing pages de alta conversão…
execution: delegated        # inline (no mesmo contexto) | delegated (subagente isolado)
allowed_tools: [read_file, create_file, web_search]
async: true                 # roda em segundo plano e avisa quando terminar
---
```

**Como uma skill é acionada:** `/nome` no chat (bypass direto, aceita nome parcial) ou classificação
semântica automática pelo modelo leve local.

**Gestão pela UI** (barra lateral → Skills):
- Clicar abre o **card** com descrição, ferramentas e recursos (divulgação progressiva).
- Habilitar / desabilitar (some para você e para o Octopus) · Excluir.
- **Upload de .zip** e **import do GitHub** — todo pacote importado passa por **auditoria de segurança**
  (`SkillAuditor`: `curl|bash`, `rm -rf`, leitura de chaves/`.env`, reverse shell, crontab/systemctl/iptables,
  injeção de prompt). Achou algo → a skill entra em **quarentena**, não é carregada.
- **Salvar conversa como skill** (rascunho) — vira ponto de partida editável.

**Gestão pela UI:** Configurações → aba **Skills** (elas saíram da barra lateral — skill é configuração do
agente, não navegação). Renomear, habilitar/desabilitar, excluir, importar `.zip`/GitHub.

**Aprendizado com crivo (2026-07-12 — mudou):** turnos com muitas chamadas de ferramenta disparam o
`LearningLoop`, que **destila o que aprendeu em CONHECIMENTO e deixa em espera**:

- **aprovado** → vira conhecimento **do projeto onde nasceu**, e entra no contexto das conversas dele;
- **reprovado** → é ignorado **para sempre**, e o dedupe por título impede que volte a ser proposto.

Está em Configurações → **Conhecimento**. Antes disso, ele criava skill sozinho e já ativava: o resultado
medido foram **11 skills auto-geradas redundantes** (duas de transcrever áudio, três de status de landing
page), 3.000 tokens de catálogo em todo turno e ruído no roteamento. Aprendizado sem crivo não é
aprendizado — é acúmulo.

**Saúde da skill** (`SkillHealthService`): execuções, falhas e falhas seguidas, contadas **em código, sem
LLM**. Skill que falha 3 vezes seguidas vira aviso. Isto substituiu a "auto-otimização DSPy/GEPA", que
reescrevia o `SKILL.md` no disco depois de TODA execução — via cérebro pago, sem aprovação, inclusive
quando a execução tinha dado certo (ou seja: parafraseava lentamente as skills escritas à mão). A
reescrita assistida, com diff e aval, está especificada em `Specs/backlog/otimizacao-de-skill.md`.

**Anti-alucinação (`PostconditionChecker`):** toda afirmação de escrita na resposta ("criei/salvei
`x.md`") é conferida **no disco** antes de a resposta sair. Arquivo inexistente volta pro modelo com uma
chance de corrigir; se ele insistir, a resposta sai com ressalva. Veio de um caso real: o Reversa anunciou
ter criado um `reconhecimento.md` que não existia — e a "correção" da época era uma regra em prosa pedindo
ao modelo que não mentisse.

**Destaques do acervo (70 skills):** família `reversa-*` (arquiteto, detetive, revisor, documentador,
precificação, D3/Highcharts, design system…), `landing-page-especialista` (com gate de QA determinística +
Lighthouse), `youtube-learner`, `system-descobrir-e-instalar-mcp`, `self-test`, `command-*` (commit, review, plan).

---

## 5. Projetos

Projeto é o espaço de trabalho persistente (referência: modelo do Manus). Cada projeto tem:

- **Instrução** — herdada por toda conversa vinculada ao projeto, dentro do trecho cacheável do prompt.
- **`project_rules.md`** — regras que o Octopus é obrigado a cumprir naquele projeto.
- **Conhecimento** — upload de documentos indexados só daquele projeto.
- **Conversas vinculadas** — histórico do projeto reunido.
- **ToDo / tarefas** — lista com progresso, atualizada a cada 5s (dá pra ver o que ele está fazendo agora).
- Fixar / excluir.

Projeto em produção: **Débora** (landing page `deboramenezes.com.br`).

---

## 6. Conhecimento e memória

| Camada | Onde vive | Para quê |
|---|---|---|
| Conhecimento **global** | Configurações → Conhecimento | Documentos que valem para tudo |
| Conhecimento **de projeto** | dentro do Projeto | Documentos só daquele trabalho |
| Memória de conversa | Postgres (`messages`) | Histórico recente |
| Memória longa | resumo destilado por conversa | O que saiu da janela não se perde |
| Fatos do usuário | `persist_user_fact` | Preferências, dados estáveis |

Indexação com embeddings + busca semântica (`search_knowledge_library`, `read_knowledge_document`).
PDF é lido de verdade (extração de texto, teto de 30k caracteres por documento).

---

## 7. Regras

- **`data/global_rules.md`** — Configurações → aba **Regras**. Valem sempre.
- **`data/projects/<id>/project_rules.md`** — dentro de cada projeto. Valem naquele projeto.

Ambas entram no system prompt (bloco de regras, antes do breakpoint de cache). Sem regras cadastradas,
nenhum bloco é injetado — não há ruído nem custo.

---

## 8. Ferramentas (~32)

| Ferramenta | O que faz |
|---|---|
| `read_file` / `create_file` / `list_directory` | Arquivos (com PathGuard) — PDF incluso |
| `download_file` | Baixa de URL pública direto pra pasta do projeto |
| `web_search` / `web_fetch` | Pesquisa e leitura de páginas (com SsrfGuard) |
| `generate_image` | Gemini / Nano Banana (fast e pro) |
| `ocr_image` | Texto a partir de imagem |
| `execute_sandbox_code` / `extract_sandbox_artifact` | Código em sandbox + resgate de artefatos |
| `save_to_knowledge_library` / `search_knowledge_library` / `read_knowledge_document` | Biblioteca |
| `persist_user_fact` | Memória de fatos |
| `upload_to_bucket` / `list_bucket` | MinIO |
| `resolve_web_domain_path` | Descobre a pasta pública de um domínio na Hestia |
| `run_landing_page_qa` | QA determinística de landing page (+ Lighthouse) |
| `run_self_tests` | Roda o catálogo de autotestes |
| `scan_project` / `search_code` | Árvore, linguagens, configs, entrypoints; busca recursiva no código |
| `browse_page` | Navegação real com Playwright (SSRF guard) |
| `render_animation` | Motion graphics → MP4 (Playwright frame-a-frame + ffmpeg) |
| `ask_model` | Consulta lateral a outro modelo (só gratuitos, por padrão) |
| `todo_write` / `todo_update` / `todo_view` | ToDo do projeto (`ToDo_List.md`) |
| `task_remember` / `task_recall` | Memória privada do tentáculo |
| `delegate_subtask` | Subagente (até 2 níveis) |
| MCP | Toda ferramenta exposta por servidores MCP configurados |

**Ferramentas OSINT** (só rendem numa investigação; ver §9.4 e `docs/OSINT_API.md`):

| Ferramenta | O que faz |
|---|---|
| `osint_scope` | Define/lê o escopo autorizado (portão fail-closed) |
| `osint_recon` | Recon via Kali no Cockpit (gateado pelo escopo) |
| `osint_graph` | Escreve/relaciona entidades no grafo do caso |
| `osint_evidence` | Guarda evidência no cofre (o quê + por quê + metadados) |
| `image_metadata` | EXIF de foto (câmera, GPS, autor) → grafo + evidência |
| `face_search` | Busca facial no PimEyes via sessão do usuário (pode parar em CAPTCHA) |
| `wigle_networks` | Redes WiFi por GPS/SSID (WiGLE, API oficial) → nós `rede` no grafo |
| `shodan` | Infra exposta: portas/serviços/CVEs de IP, subdomínios de domínio (gateado pelo escopo) |

**Ferramenta de leitura tem cache** (`ToolCache`): a mesma leitura não vai duas vezes à API dentro da
tarefa. Validado por `mtime` — editou o arquivo por fora, o cache morre na hora — e qualquer **escrita**
derruba o cache do workspace. O padrão que motivou isso é real: no trace do `/reversa`, o modelo lia o
`package.json`, listava o `src/`, e relia os dois três passos depois.

**Não existe** ferramenta de shell livre. Decisão de segurança deliberada: um agente com `bash` irrestrito
em VPS de produção é risco alto demais. Está pendente de decisão sua.

---

## 9. Integrações

### 9.1 Google (OAuth) — Gmail, Agenda, Drive
Conectado em Configurações → Integrações (client id/secret no cofre, refresh token
renovado sozinho, callback protegido por `state`). Ferramentas: `gmail_search`,
`calendar_events`, `drive_search` — só leitura por ora (escrita é decisão separada).
Entram no catálogo na conexão, sem reiniciar.

### 9.2 n8n — o braço determinístico (500+ conectores)
O Octopus comanda o n8n pela API pública dele (API key no cofre). Ferramentas:
`n8n_list_workflows`, `n8n_run_workflow`, `n8n_manage_workflow` (ativar/desativar, ver
execuções). O Octopus decide; o n8n executa a integração mecânica. Configuração em
Integrações.

### 9.4 Provedores OSINT externos (Configurações → OSINT)
O módulo OSINT tem conhecimento **global** (catálogo de técnicas, treinável por URL/PDF/texto/áudio/
YouTube) e projetos marcados como **investigação** (o cadeado), **isolados** entre si (escopo,
conhecimento, grafo, evidências e contexto próprios). Provedores plugados, cada credencial no cofre:

- **PimEyes (busca facial)** — não tem API; o Octopus usa a **sessão logada do usuário** (cookies
  exportados pelo Cookie-Editor, colados na UI). Playwright dirige a busca. **Para em CAPTCHA** — não
  o resolvemos. Tool `face_search`.
- **WiGLE (redes WiFi)** — API oficial (Basic auth com o token "Encoded for use" da conta). A partir do
  GPS de uma foto, lista redes da área; vira nó `rede` no grafo. Tool `wigle_networks`.
- **Shodan (infra exposta)** — API oficial (API key). Recon **gateado pelo escopo**. No plano grátis
  (oss), `host` e `dominio` funcionam; a busca por dork exige créditos pagos. Tool `shodan`.
- **YouTube (treino do catálogo)** — `yt-dlp` extrai a legenda para destilar em técnicas. O YouTube
  bloqueia IP de VPS; precisa dos **cookies** do youtube.com (mesma via do PimEyes).

Toda a superfície OSINT é REST e desacoplada do front — referência completa em `docs/OSINT_API.md`.

### 9.3 MCP (Model Context Protocol)

Configurações → aba **Integrações**: visualizar, incluir, editar e excluir servidores MCP
(`data/mcp-servers.json`), com **recarga a quente** — as ferramentas do servidor entram e saem do
registro sem reiniciar o Octopus. Servidor marcado como `disabled` não é conectado.

---

## 10. Segurança

- **CredentialVault** — cofre criptografado (`data/vault.enc`, chave mestra `VAULT_MASTER_KEY` no `.env`).
  **Todas** as chaves de API vivem lá; o `.env` foi limpo. Leitura cai para `process.env` só como último recurso.
- **PathGuard** — escrita restrita a `/root/Octopus` e às pastas públicas Hestia explicitamente liberadas.
- **SsrfGuard** — bloqueia requisições para rede interna (resolução de DNS guardada).
- **SkillAuditor** — auditoria de todo pacote de skill importado (quarentena automática).
- **SpendCircuitBreaker** — teto de gasto.
- Sem tool de shell (ver §8).

---

## 11. Operação

```bash
# deploy (compila → dist.next → troca → reinicia → smoke test → rollback automático se falhar)
./deploy.sh

# serviço
systemctl status octopus.service
journalctl -u octopus.service -f

# testes
npx tsc --noEmit                                   # tipos
npx jest tests/unit                                # 145 testes / 20 suítes
npx tsx tests/self-test/runner.ts                  # catálogo Playwright (telas|cruds|dados|interfaces)
```

Produção roda `node dist` (sem hot-reload). O `deploy.sh` faz rollback sozinho se o smoke test falhar.

---

## 12. API HTTP (principais rotas)

```
POST /api/login                       POST /api/chat        → { response, durationMs, model }
GET  /api/status                      POST /api/voice
GET  /api/activities

GET/POST/PATCH/DELETE  /api/v1/conversations
GET/POST/PATCH/DELETE  /api/v1/projects            GET/PUT /api/v1/projects/:id/rules
GET/PATCH/DELETE       /api/v1/skills[/:id]        POST /api/v1/skills/upload
POST /api/v1/skills/import-github                  POST /api/v1/skills/from-conversation
GET/POST               /api/v1/knowledge[/upload]
GET/PUT                /api/v1/rules
GET/PUT                /api/v1/mcp-servers
GET/PUT                /api/v1/model-preferences
GET                    /api/v1/models              POST /api/v1/models/probe
GET                    /api/v1/providers-status    GET  /api/v1/spend-status
GET                    /api/v1/personality
```

---

## 13. Limitações honestas (hoje)

- **Sem tool de shell** — deliberado, aguardando sua decisão.
- **Pool de modelos degradado (2026-07-13):** Anthropic/OpenAI/DeepSeek sem crédito e Gemini estourou o
  teto; de pé só Groq, um free do OpenRouter e os locais. É a **raiz** dos sintomas de investigador que
  recusa ou "esquece o caso" — modelo fraco na ponta, não bug. Ver `BACKLOG.md` P0-1/P0-2.
- **Modelos locais (qwen/gemma):** ótimos e grátis no determinístico (classificação, resumo, extração,
  tool simples), **marginais** no raciocínio longo, em não-recusar OSINT e em manter a persona. O gemma
  é lento (raciocina antes de escrever). "Dão conta?" — para o núcleo do investigador, hoje **não sem
  ajuda de um modelo forte**; para as tarefas de fundo, sim. (Bench formal no backlog.)
- **Busca facial (PimEyes)** para em **CAPTCHA** e depende de cookies do usuário; **Shodan grátis** não
  faz busca por dork; **YouTube** exige cookies do usuário (o IP da VPS é bloqueado) — reativado via
  `yt-dlp --cookies`.
- **Google Drive** — falta o *client secret* real (`GOCSPX-…`).
- **Aprendizado não é automático, por design**: o Octopus propõe conhecimento, você aprova. Nada entra no
  contexto sem seu aval.

## 14. Qualidade: como o Octopus se prova

| Suíte | O que cobre | Como rodar |
|---|---|---|
| **254 testes unitários** | PathGuard, SsrfGuard, disjuntor, cadeia de fallback, compactação de contexto, cache de ferramenta, verificador de pós-condição, livro-caixa, crivo do aprendizado, ToDo, memória de tentáculo | `npx jest tests/unit` |
| **19 self-tests de tela** | Playwright contra a VPS **de verdade**: login, árvore projeto→chat, Configurações (incl. aba Personalidade), seletor com preço, caixa do chat (Enter envia, Shift+Enter quebra linha), gaveta do celular, Modo Prosa | `npx tsx tests/self-test/runner.ts telas` |

Os self-tests já pegaram bug real mais de uma vez — inclusive um teste que **passava por acaso** (clicava
no centro de um backdrop que fica *debaixo* da gaveta).

## 14a. Orquestrador — o Octopus como agente que conduz trabalho (Fase A)

Antes, o Octopus era um editor de arquivos: em 7 dias de log, 0 uso das ferramentas de
organização (todo, delegação, memória de tentáculo). A causa não era o modelo — era a
falta de uma camada que CONDUZ o trabalho, em vez de pedir organização em prosa (que o
modelo ignora). O orquestrador é essa camada.

**Como funciona** (`src/core/orchestrator/`):

1. **Planejar** (LLM, 1 vez): o modelo é obrigado a chamar `submit_plan(tarefas[])` — uma
   saída ESTRUTURADA, não prosa. Cada tarefa tem um tentáculo e uma lista de dependências.
2. **Validar** (código): o grafo tem ciclo? dependência fantasma? tentáculo inexistente?
   Recusa na entrada, não na metade.
3. **Executar** (código, zero LLM): percorre o grafo. Tarefas SEM dependência entre si
   rodam em **paralelo** (`Promise.all`); as que dependem esperam — **sequência**. O
   "paralelo ou sequencial" emerge das dependências, não de configuração.
4. **Verificar** (código): ao terminar, a pós-condição confere no disco o que a tarefa
   afirma ter criado. Uma tarefa que mente é reprovada e não envenena as que dependem dela.
5. Um galho que falha bloqueia **só** quem depende dele, em cascata — o resto segue.

**Estado no banco, não em arquivo** (tabelas `plans` + `plan_tasks`). O motivo decisivo é
concorrência: como as tarefas rodam em paralelo, cada término grava só a sua linha
(`UPDATE` atômico) em vez de regravar um JSON inteiro (corrida de leitura-escrita, uma
atualização some). O `ToDo_List.md` vira projeção legível, não a fonte da verdade.

**Como acionar:**
- No chat: `/plano <objetivo>` (roda em segundo plano, notifica ao fim).
- Por API (PWA/n8n/mobile): `POST /api/v1/plans/preview` (planeja sem executar — **Plan
  Mode**), `POST /api/v1/plans` (planeja e executa), `POST /api/v1/plans/:id/run`,
  `GET /api/v1/plans/:id` (grafo ao vivo, a PWA faz polling), `GET /api/v1/projects/:id/plans`.
- Na PWA: Configurações → aba **Planos**. Escreve o objetivo, revisa o grafo, aprova, e vê
  cada tentáculo acender ao vivo.

## 14b. Tarefas agendadas (cron)

A tarefa agendada é **uma conversa que o Octopus tem sozinho**: roda pelo mesmo pipeline do chat (skills,
ferramentas, projeto, memória), com o texto vindo do agendamento em vez de vir de você. O resultado chega
como notificação (web + Telegram). Configurações → aba **Agendadas**.

- **Fuso explícito** (`America/Sao_Paulo` por padrão). A VPS roda em **UTC**: um "todo dia às 7h" avaliado
  no relógio do servidor dispararia às 4h da manhã. O pior bug de agendamento é o que *funciona*, só que
  na hora errada, todos os dias, sem erro nenhum no log.
- **Cron em código**, sem dependência (`src/services/CronExpression.ts`): 5 campos, `*/n`, listas,
  intervalos, apelidos (`@daily`), regra do Unix para dia-do-mês × dia-da-semana, e horário de verão de
  graça (quem resolve é o `Intl`).
- **Validado na criação**, não na hora de disparar: expressão com erro de digitação é recusada na tela, e
  não descoberta de madrugada.
- **Sem sobreposição**: tarefa que ainda está rodando não é disparada de novo.
- **Sem enxurrada**: se o serviço ficou fora 3 dias, uma tarefa horária não dispara 72 vezes — roda **uma**
  e remarca o relógio. Agendamento não é fila de mensagens.
- **▶ Agora**: executa na hora, pra testar sem esperar o horário chegar.

`GET/POST /api/v1/scheduled-tasks`, `PATCH/DELETE /api/v1/scheduled-tasks/:id`, `POST …/:id/run`.

## 15. Próximos passos combinados

1. **Caixa de entrada de decisões unificada** — hoje conhecimento em espera, skills-rascunho e futuros
   diffs de skill são painéis diferentes pra mesma pergunta ("aprovo ou não?").
3. Skill de **OSINT / dossiê** (Fase 3.2, motor `osint-d2`).
4. Painel de **preview e publicação** de site.
5. Conectores OAuth (Drive) — travado no *client secret*.
6. Decisão sobre a **tool de shell**.

---

*Documento vivo. Ao mudar o comportamento do Octopus, atualize aqui — é o mapa que o dono do sistema lê.*
