O cofre deixa de ser apenas o gestor de credenciais e passa a ser a interface gráfica da memória: as telas de corpus entram no mesmo app Starlette, com a mesma sessão, a mesma CSP fechada, o mesmo i18n PT/EN e a mesma auditoria. Nenhuma porta nova, nenhuma dependência nova.
Telas
| Rota | O que mostra |
|---|---|
/memories |
Lista com facetas e cursor real |
/memories/{id} |
Payload completo, ativação ACT-R, cadeia de versões, histórico, entidades vinculadas, proveniência de documento |
/search |
Busca semântica com as_of, event_date e recordação histórica |
/queue |
Fila de ingestão: profundidade, progresso por trecho, dead letters |
/entities |
Entity store com identidade normalizada |
/users |
Usuários e tokens (extraídos do painel) |
Por que cada superfície lê de onde lê
Não é preferência — é restrição medida do servidor:
- a navegação vai ao Qdrant direto porque
get_memoriesé primeira-página-com-teto e não expõe cursor: num corpus maior que o teto, o excedente é inalcançável pela tool; - os campos de ACT-R não passam pela whitelist de metadata do
search_memories; - o entity store não tem caminho MCP nenhum (
list_entitiesfaceta a collection principal, e as tools de grafo falam com um Neo4j que não está de pé); - fila e histórico saem do SQLite em
mode=ro+query_only; - a busca é a exceção deliberada e vai pelo MCP, porque o valor está no pipeline (denso + BM25 + reranker + ativação +
as_of), não no índice. Sempre comreinforce=false: navegar num console de operador não é um re-encontro da memória, e contá-lo enviesaria o ranking que a própria tela exibe.
Três achados do servidor real que moldaram o código
facet()exige índice de payload. Campos comodomainnão têm e devolvem400. As facetas são contadas em Python sobre um scroll projetado. Filtrar por campo sem índice funciona; só facetar é que não.- A paginação é por valor, e o valor não é único. Com
order_byo scroll não devolvenext_page_offset,start_fromé inclusivo, e os trechos de um documento nascem no mesmocreated_at— um instante pode ter mais pontos que a página inteira. O cursor carrega os ids já entregues naquele instante: guardar só o timestamp repetiria o grupo para sempre, e um contador dependeria de uma ordem que a API não promete. - O SDK esconde a causa. O transporte roda num task group, então um token recusado chegava como
ExceptionGroup: unhandled errors in a TaskGroup. A causa é desembrulhada antes de virar mensagem.
Segurança e robustez
- Telas de corpus estritamente de leitura — um teste lê o fonte do leitor e reprova qualquer verbo de escrita
- Fonte indisponível não derruba a UI: vira card na tela, e as telas de credencial seguem servindo
- O pacote sai com defaults neutros; o escopo e a collection de cada instalação vivem no env file, e o
.env.exampleavisa que deixá-los em branco rende uma tela vazia sem erro nenhum
Gate
1278 testes verdes, paridade i18n PT/EN travada por teste, e smoke de 16 verificações contra corpus real.