Princípios de design que governam decisões. Quando há conflito entre features e princípios, princípios ganham.
Não cache. Não state em memória. Não objetos serializados. Append-only event log. Imutável. Replayable. Auditable.
Consequência: todo state derivado é reconstruível a partir do log. Se algo não pode ser reconstruído do log, está errado.
Não negociável.
O harness é uma função pura: step(events, tools, ctx) → AsyncIterator[Event]. Pode morrer a qualquer instante. O próximo wake() reconstrói tudo a partir do log.
Consequência: sem sessões "agarradas" a uma máquina. Sem state in-memory entre invocações. Sem locks distribuídos. Escala horizontalmente sem cerimônia.
Não negociável.
Containers são provisionados preguiçoso (na primeira tool call que precisa) e descartáveis. Morte do container = tool_call error, não falha de sessão.
Consequência: TTFT baixo (não espera container). Custo baixo (containers só quando necessários). Resiliência alta (qualquer container morre, ninguém liga).
Não negociável.
O sandbox é invocado via execute(name, input) → string. O harness não sabe se por trás é Docker, sandbox-runtime, Firecracker, gVisor, máquina física ou Pokémon emulator.
Consequência: trocar de runtime de sandbox é trocar de adapter. Sem reescrever harness, sem reescrever frameworks.
Não negociável.
Tokens OAuth, API keys, secrets vivem num vault separado. O harness chama tools autenticadas via proxy. O proxy injeta credenciais no nível do request HTTP. O harness e o sandbox nunca veem o token real.
Consequência: prompt injection que tenta exfiltrar credenciais não tem o que exfiltrar. Audit trail mostra cada uso. Rotação não exige redeploy.
Não negociável.
Wake não importa LangGraph. LangGraph não importa Wake. Existe um adapter no meio que traduz.
Consequência: atualização de LangGraph não quebra Wake. Mudança em Wake não quebra LangGraph. Adapters são versionáveis independente do framework.
Negociável apenas com motivo arquitetural muito claro.
Sandbox: usa sandbox-runtime. Vault: usa Infisical Agent Vault. Model routing: usa LiteLLM. MCP: usa o protocolo oficial. Agent definition: usa Open Agent Specification.
Consequência: o código próprio de Wake é menor. Vulnerabilidades resolvidas upstream nos beneficiam. Bibliotecas maduras compartilhadas com ecossistema.
Aplicável sempre que existir alternativa OSS razoável.
Endpoints, status codes, schemas de request/response idênticos onde possível. Um dev que sabe Managed Agents consegue usar Wake sem reler docs.
Consequência: drop-in migration. Adoção mais fácil. Ferramentas ao redor (SDKs, dashboards) funcionam nos dois.
Aplicável até o ponto onde divergir é claramente melhor.
Replay produz mesmo resultado dado mesmos inputs. Onde determinismo é impossível (LLM amostragem, tempo, IO externo), Wake snapshota.
Consequência: debugging via replay é confiável. Compliance audit é confiável. Side-by-side comparison de agentes é confiável.
Aplicável a tudo no caminho hot.
Toda decisão estrutural passa por um documento (spec, RFC) antes de virar código. Documento é PR-eável. Comunidade revisa antes da implementação.
Consequência: menos retrabalho. Comunidade compra a tese antes do código existir. Specs viram padrão; código vira referência.
Aplicável a HarnessAdapter, event schema, tool ABI, API REST, qualquer interface pública.
pip install wake-ai && wake server --local && wake run "hello" precisa funcionar em <2 minutos sem ler docs.
Consequência: decisões padrão precisam ser inteligentes. Configuração é progressiva: nada → mínimo → completo.
Aplicável a CLI, SDK, exemplos, docs.
Casos comuns: rodar um Claude SDK simples; ouvir eventos; pegar resultado final. Cada um precisa ser uma linha de código.
result = wake.run("refactor this file", agent="coding-bot")Casos avançados (multi-framework, vault customizado, sandbox plugável) ficam atrás de configuração explícita.
Princípio Rails-y: "convention over configuration" para 80% dos casos.
Day 1: Claude (Anthropic API direto). Day 2+: outros providers via LiteLLM ou adapter.
Motivação: semântica de tool use varia entre providers. Caching/thinking/skills são Claude-only. Tentar abstrair tudo no Day 1 = vazamento ou simplificação demais.
Honesto: Wake é "Claude-first, BYO-LLM possible." Não é "neutral multi-provider."
Multiagent (coordinator + workers), outcomes (LLM-as-judge), memory (long-term facts) — todos são úteis. Todos podem ser construídos em cima das primitivas. Nenhum é primitiva.
Consequência: roadmap não promete essas coisas no core. Elas podem virar pacotes (wake-multiagent, wake-outcomes, wake-memory) ou plugins.
Se uma feature não está documentada, ela não existe. PRs que mudam comportamento sem atualizar docs são rejeitados.
Aplicável desde o pre-alpha.
Algumas decisões custam muito reverter depois. Listadas explicitamente para tomar com peso:
- Event schema canônico — quase impossível mudar quebrando, exige major version bump
- HarnessAdapter signature — idem
- Session status enum — frameworks dependem disso
- Tool ABI — todas as tools dependem
- API REST contract — clientes dependem
- Linguagem do runtime principal — Python vs Go vs Rust define ecosistema
Para essas, exigimos: spec escrita, RFC público, ≥2 semanas de revisão, ≥3 adapters de referência implementados antes de v1.0.
- ❌ "Suportar todos os frameworks" via interface mais larga e leaky
- ❌ Embutir LLM routing dentro do runtime (delegamos a LiteLLM)
- ❌ Embutir vector store dentro do runtime (delegamos a quem quiser)
- ❌ UI/dashboard como parte do core (vira pacote separado)
- ❌ Optimization automática de prompt (não é nosso problema)
- ❌ Fine-tuning, RAG, vector search (não é nosso problema)
- ❌ Marketplace de agents (não é nosso problema)
- ❌ Billing/quota (não é nosso problema)
- ❌ Empurrar conta SaaS no usuário
Wake é substrato. Quem quer SaaS por cima constrói por cima.