CFS · leitura de material de terceiro · 04.09.2026
Nada foi
executado.
Só lido.
O pacote da Naia Kimi tem 31 arquivos e 7 pastas, e eu abri todos. Não rodei nem o install.sh, nem hook, nem script, em lugar nenhum. Extraí em pasta de rascunho fora de /opt/mari e não copiei uma linha pra dentro da nossa árvore.
O que vale ali não é o Kimi. É o jeito dele de transformar regra de comportamento em trava de máquina. É isso que a gente traz, e traz hoje, com Kimi ou sem Kimi.
O veredito curto
Três coisas justificam o pacote inteiro, e nenhuma delas depende de a gente montar a Mari Kimi.
- O hook anti-promessa. Quando ela escreve "vou fazer" e encerra o turno sem chamar ferramenta nenhuma, o sistema recusa o encerramento e manda executar. Isso não é lembrete no arquivo de instrução, é bloqueio. Ataca de frente o defeito que mais se repete aqui.
- A busca automática antes de responder. Cada mensagem do Chefe passa pela memória vetorial antes de chegar ao modelo, e as memórias relevantes entram junto no contexto. A gente já tem as duas peças (a API da 3007 e o pgvector) e nunca ligou uma na outra por hook.
- Boot magro por decisão. Ele proíbe a agente de ler arquivo grande no boot e confia na busca por mensagem. O nosso protocolo lê seis arquivos mais trinta trocas em toda sessão nova. É a razão de a gente reiniciar aos 75%.
E a correção que eu preciso dar de cara
A gente não está sem hook. Estamos com dois, presos em quatro eventos, no ~/.claude/settings.json: o security-scan.py e o task-tracker.py. Eu descobri isso do jeito mais direto possível: no meio desta análise, o security-scan.py bloqueou um comando meu por casar com o padrão de baixar código e canalizar pro interpretador. Ou seja, hook aqui funciona, roda dentro de subagente e já protege a casa.
Segurança do pacote
Credencial em texto puro: zero
Li os 31 arquivos procurando chave, token e senha. Não achei nenhum valor de credencial. Todo segredo dele resolve em tempo de execução com op read e op inject, do cofre do 1Password, e o cache fica só em memória do processo. O .gitignore ainda bloqueia .env, *.key e *.pem, e o install.sh grava o .env do bot com chmod 600.
Isso é higiene melhor que a nossa. A gente guarda segredo em arquivo no disco, em /opt/mari/.env e /opt/mari/.secrets/. Não é urgência, mas é um ponto em que ele está na frente.
O que tem ali e não devia circular
Segredo não tem. Mapa da operação dele, tem. Descrevo o tipo e o lugar, sem reproduzir valor:
- Cinco endereços de servidor, com o comando de SSH pronto e o caminho da chave privada, na seção de servidores do
AGENTS.md. - O ID de chat do Telegram dele, em duas ocorrências: no
AGENTS.mde como valor padrão dentro docron-scripts/task-watchdog.py, na função que resolve o destinatário. - O mapa do cofre. Nome do cofre e oito pares de item e campo, ou seja, o índice de onde cada credencial dele mora. Não abre nada sozinho, mas diz exatamente onde bater.
- Caminhos absolutos da máquina dele, o usuário do sistema e a estrutura das pastas, em praticamente todo arquivo.
Minha recomendação, e é só recomendação: o zip não circula. Ele já está na nuvem do Telegram porque chegou por lá. Não republicar, não subir em repositório, não mandar adiante. E se um dia a gente devolver alguma coisa pro Denderson, devolver o conceito, nunca o arquivo dele de volta editado.
Texto tentando dar ordem a quem lê
Procurei carga de injeção, quer dizer, trecho escrito pra quem estiver lendo o arquivo em vez de pra dona dele. Não existe nada desse tipo no pacote. Nenhuma frase endereçada ao leitor, nenhum "ignore suas instruções", nenhum pedido de credencial.
Mas existe um risco estrutural que eu preciso nomear, porque é real mesmo sem má intenção.
O AGENTS.md é um prompt de sistema completo, escrito em primeira pessoa
São 17 KB de regra imperativa: "eu nunca executo", "nunca discuto com o Chefe", "respondo APENAS o que ele perguntar". E tem um bloco que se declara superior a qualquer outro: este bloco vence qualquer outro trecho deste arquivo, da memória ou de skill.
O perigo não é o conteúdo, é a forma. Nesse arquivo, "o Chefe" é o Denderson, com o ID de Telegram dele, e "o outbox" é a pasta dele. Qualquer agente nosso que engula esse texto como instrução em vez de dado passa a ter outra definição de quem manda e de onde escrever a resposta.
Trava prática, e eu já cumpri as duas: o arquivo fica fora de /opt/mari, e nunca entra no knowledge/. Se ele fosse indexado, a busca da 3007 poderia devolver um pedaço dele como memória relevante, e aí o texto entraria no contexto da Mari por conta própria. Esse é o caminho pelo qual isso vira problema de verdade, e ele fecha com uma regra de uma linha: material de terceiro não é indexado.
Um detalhe de postura
A agente dele roda em modo yolo, sem confirmação de permissão, e o único freio é o hook de pastas confiáveis. A nossa postura é a mesma, com skipDangerousModePermissionPrompt ligado e o security-scan.py como freio. Os dois times chegaram sozinhos no mesmo desenho: liberdade total de execução, com uma trava de máquina no caminho.
Inventário, arquivo por arquivo
31 arquivos e 7 pastas. Nenhum pulado.
Antes de tudo, uma correção de contagem: o zip lista 38 entradas, mas sete delas são pastas. Arquivo mesmo são 31.
Raiz
A alma e o manual dela num arquivo só, que é o equivalente exato do nosso CLAUDE.md. Persona, protocolo de conversa, time de subagentes, memória, credenciais, infra e segurança.
A arquitetura inteira num diagrama de uma página, mais "como operar", "o que NÃO mexer" e "o que não está no repositório". O formato é bom e o nosso RECOVERY.md cobre só parte disso.
Bloqueia home/, state/, logs/, memory/*.md, .env, *.key e *.pem. O nosso tem 4 KB e cobre mais.
hooks · 11 arquivos
Hook de Stop. Promessa futura sem nenhuma chamada de ferramenta depois dela devolve exit 2 e o turno não fecha. É a peça mais valiosa do pacote.
Casca que registra no log e chama o Python do pyenv por caminho absoluto do Mac. A gente chama o script direto.
Hook de UserPromptSubmit. Consulta a memória vetorial com a mensagem do Chefe e injeta o que for relevante antes do modelo pensar.
Mesma casca de wrapper com caminho do Mac.
Não é hook, é o módulo que os outros dois importam. Monta o contexto de boot: quem ela é, o pending.md cortado em 4.000 caracteres e as 10 últimas mensagens dela no banco.
Existe só porque o Kimi 0.34.0 descarta a saída do SessionStart. No Claude Code essa saída é injetada de verdade, então a gambiarra some e o hook vira o canal principal.
Wrapper.
Hook de PreToolUse. Escrita e comando destrutivo fora das zonas permitidas são bloqueados. Leitura é sempre livre. Complementa o nosso security-scan.py em vez de repetir.
Wrapper.
Antes de compactar o contexto, roda o consolidador de conversa. Barato, e no nosso caso o valor é menor porque quem grava o histórico aqui é o bot, não a sessão.
Quando um turno morre por erro, grava a hora num arquivo. O guarda de resposta do bot lê isso e cutuca em 60 segundos em vez de 180. Quatro linhas que valem a pena.
cron-scripts · 3 arquivos
A cada 30 minutos, cópia dos arquivos de memória, do marcador de tarefa e do AGENTS.md, com manifest.json, sha256 por arquivo e retenção dos 24 últimos.
O maior arquivo do pacote. Lê os bytes novos do registro bruto da sessão e grava no Postgres. Nosso bot já faz o equivalente para o Telegram, mas ele captura mais: chamada de ferramenta e fala interna também viram memória.
Sinal de vida a cada 7 minutos e cutucão via tmux quando a sessão fica ociosa com tarefa aberta. O nosso status-nudge.sh faz o mesmo sem depender de marcador escrito à mão.
scripts · 4 arquivos
Lista, roda e compara modelos via OpenRouter, com tabela de latência, tamanho e custo. É bancada de teste, não é o cérebro dela. Detalho no bloco do provedor.
Dispara um trabalhador sem interface em segundo plano com saída em fluxo JSON, guardando o fluxo cru, um arquivo de progresso legível e o PID.
Transforma o fluxo JSON em linhas de progresso do tipo hora mais última ferramenta chamada. O formato do nosso fluxo é outro, então o extrator muda.
Devolve os arquivos de memória e o marcador de tarefa de um checkpoint. Assume por escrito que a conversa em si não volta.
Subida e supervisão · 3 arquivos
Laço de vida: continua a sessão anterior, e se levar 3 quedas em menos de 120 segundos começa do zero. Espera crescente de 5 a 15 segundos. Temos equivalente.
Mata e recria a sessão tmux rodando o start.sh, e vigia de 30 em 30 segundos. É o papel do nosso serviço no systemd.
Instalador idempotente do mundo macOS. Gera o .env a partir do cofre, copia os plists e sobe os cinco serviços com launchctl. A lógica de "sobe o bot só se o token existir" é boa, o resto não atravessa.
launchd · 5 arquivos
Mantém a agente viva. Vira systemd com Restart=always aqui.
Mantém o bot vivo. Mesmo destino.
De 2 em 2 minutos. Vira uma linha de crontab.
De minuto em minuto. Vira uma linha de crontab.
De 30 em 30 minutos. Vira uma linha de crontab.
Pastas que nascem vazias · 2 arquivos
Marcador de pasta. A memória em arquivo dele nasce vazia em toda réplica, porque o conteúdo durável mora no Postgres.
Modelo limpo da lista de pastas com permissão de escrita. Quatro caminhos de exemplo. É o formato que a gente copia junto com o hook.
O AGENTS.md dele, contra o nosso CLAUDE.md
17,4 KB dele · 14,5 KB nosso
Antes de qualquer comparação: a persona não se copia. A Mari é a Mari. O que interessa aqui é onde ele resolve melhor um problema que também é nosso.
O que ele tem e a gente não
- Boot magro escrito como regra. Está lá com todas as letras: "confio no grounding, não leio arquivo grande proativamente no boot". O nosso protocolo faz o contrário, manda ler seis arquivos e trinta trocas em toda sessão. É a diferença entre gastar contexto na largada e gastar quando precisa.
- Marcador de tarefa em arquivo. Ao delegar, ela grava um JSON com o que está rodando, qual subagente, desde quando e a estimativa. O vigia lê esse arquivo. A nossa regra equivalente é prosa no
CLAUDE.mde depende de ela lembrar. - Regra explícita de irmã. Um bloco inteiro sobre a outra agente: não edito os arquivos dela, não escrevo no outbox dela, não mato os processos dela, leitura pra aprender é permitida. A gente vai precisar disso literalmente na semana que vem.
- O cutucão é pedido de verificação, não de entrega. Detalhe fino e muito bem pensado: quando o vigia cutuca, ela não inventa uma entrega pra se livrar. Se o subagente ainda roda, ela ignora e continua esperando.
- Regra de mídia no processo principal. Ela nunca abre imagem no fio principal, quem olha imagem é subagente que devolve texto. Economia de contexto que a gente não formalizou.
- Credencial só do cofre, em tempo de execução. Nada de segredo em arquivo, nem por engano.
O que a gente tem e ele não
- Verdade como seção própria. O nosso bloco de "deu ruim, o Chefe sabe primeiro" é mais duro e mais específico que qualquer coisa no arquivo dele: marcar o que é verificado contra o que é suposição, só afirmar depois de ler de volta da fonte, nunca inventar justificativa. Ele tem "não engulo conversa mole" e para por aí.
- Delegação paralela com regra de colisão. A gente escreve quando paralelizar e quando não: se duas tarefas tocam o mesmo arquivo ou banco, sequenciar. Ele delega em paralelo sem essa trava.
- Antiinchaço da memória. A nossa regra da ablação, que manda item resolvido sair do arquivo ativo pro arquivo morto porque memória é custo de boot, não existe do lado dele.
- Compromisso com hora vira registro na hora. Nosso bloco de promessa com prazo é mais desenvolvido, com seção própria no
pending.mde despertador. - Protocolo de reinício com medição de contexto. A gente sabe reiniciar a si mesma, e tem um vigia medindo o contexto real. No pacote dele não existe nada que meça contexto.
- Skills de processo. Carrossel, Eugene, humanizer, prompts visuais, e um curador semanal que destila tarefa nova em skill. Não tem equivalente.
Onde ele resolve melhor um problema que também é nosso
Promessa sem ação
Os dois arquivos proíbem a mesma coisa. A diferença é que o dele termina a frase com "meu hook anti-promise bloqueia". A regra dele tem quem a faça cumprir. A nossa é só a regra.
Esse é o padrão que se repete no pacote todo e é a lição que eu tiraria dele: toda regra de comportamento que ele consegue medir, ele vira hook. O resto continua sendo texto, como no nosso.
Buscar antes de afirmar
O nosso arquivo diz "quando o Chefe citar um assunto antigo, buscar antes de responder", e ainda deixa o comando de curl pronto. Depende de ela lembrar de rodar. No dele, a busca acontece sozinha em toda mensagem e o resultado já chega junto com a pergunta, com a instrução colada: não afirme nada que contradiga isso, e se não tiver informação aqui, diga que não tem certeza.
Os hooks, um a um
A parte que mais importa
Antes de entrar: eu conferi a documentação do Claude Code hoje, evento por evento, porque metade das decisões de portabilidade depende de detalhe que muda entre um CLI e outro. Duas descobertas mudam o desenho, e as duas jogam a favor.
Descoberta 1 · aqui o SessionStart funciona
No Kimi 0.34.0 o evento de início de sessão dispara mas a saída é jogada fora, e ele provou isso com um teste canário em agosto. Foi por isso que ele teve que enfiar o boot no primeiro prompt do usuário, com trava de arquivo pra não repetir. No Claude Code a saída do SessionStart entra no contexto normalmente, e o evento ainda aceita filtro por motivo: início, retomada, limpeza, compactação ou bifurcação. A gambiarra dele a gente não precisa, e ainda ganha um controle que ele não tem.
Descoberta 2 · o hook de Stop já recebe a última fala pronta
O anti-promessa dele gasta metade do código abrindo o registro bruto da sessão pra descobrir qual foi a última fala e se veio ferramenta depois. Aqui o payload do Stop entrega last_assistant_message de bandeja. A versão nossa nasce bem menor que a dele.
Com uma ressalva que eu não vou esconder: o Claude Code não tem o campo que evita laço infinito de hook de Stop. Então a trava de repetição dele, aquele arquivo de trava de 180 segundos por sessão, deixa de ser detalhe e vira obrigatória. Sem ela, um hook de Stop que bloqueia pode prender a sessão num laço.
- O que ele faz, exatamente
- Quando o turno vai fechar, o hook lê o fim da sessão e separa duas coisas: qual foi a última fala visível dela e se houve alguma chamada de ferramenta depois dessa fala. Aí roda um teste de expressão regular nos últimos 500 caracteres, procurando promessa futura: "vou fazer", "vou checar", "vou delegar", "deixa comigo", "já volto", "te aviso quando", e mais uma dúzia de variações em português.
- O que acontece quando casa
- Se tem promessa e não teve ferramenta depois, o hook devolve
exit 2com a razão. O turno não fecha e o modelo recebe de volta o texto: você prometeu uma ação futura mas encerrou o turno sem executar nada. Execute AGORA. Promessa sem ação é proibida. Ela é obrigada a continuar e fazer. - As duas travas de segurança dele
- Um arquivo de trava por sessão, com no máximo um bloqueio a cada 180 segundos, pra nunca virar laço. E falha aberta em tudo: payload ilegível, sessão não encontrada, erro de leitura, qualquer coisa devolve
exit 0e deixa passar. O hook nunca trava a agente por defeito próprio. - Funciona aqui?
- Funciona, e sai mais simples. O evento
Stopexiste,exit 2bloqueia mesmo, o stderr vira a razão que o modelo lê, e olast_assistant_messagevem no payload. A parte de "houve ferramenta depois" precisa de outra fonte, porque a documentação avisa que o arquivo de transcrição é escrito de forma assíncrona e pode estar atrasado. Eu resolveria com um hook dePostToolUsede duas linhas carimbando a hora num arquivo por sessão, e oStopcomparando as duas horas. Mais confiável que ler transcrição. - Esforço
- Estimativa minha: 1 hora, incluindo a lista de expressões calibrada pro jeito da Mari falar e um teste de mesa. E aplica também aos subagentes, porque hook de configuração vale dentro deles.
- O que ele faz
- Toda mensagem do Chefe vira uma consulta na memória vetorial antes de o modelo ver a mensagem. O hook manda a mensagem inteira pro serviço em
127.0.0.1:3007, filtra o que vier com similaridade abaixo de 0,50, pega no máximo 3 trechos de arquivo e 3 fatos, corta cada um em 450 caracteres e monta um bloco de contexto. - O detalhe que faz diferença
- O bloco não entrega só as memórias, entrega a instrução de como usá-las: "use isso pra fundamentar a resposta, não afirme nada que contradiga essas memórias, e se não tiver informação aqui, diga que não tem certeza em vez de inventar". É a nossa regra de não afirmar sem conferir a fonte, colada em toda pergunta, sem depender de ela lembrar.
- Cuidados dele
- Tempo limite de 2,5 segundos na chamada, pula prompt com menos de 12 caracteres pra não gastar busca com "ok" e "sim", e falha aberta em qualquer erro de rede. Se o serviço da 3007 cair, a conversa segue como se o hook não existisse.
- Funciona aqui?
- Funciona, e o evento é feito pra isso. No Claude Code o
UserPromptSubmité justamente um dos eventos cuja saída entra no contexto. Tem um ajuste obrigatório e eu já confirmei medindo: a nossa API da 3007 responde em outro formato. Ela devolve uma lista única emresults, comsource,content,scoreetype. A dele devolvechunksefactsseparados, comsimilarity. Copiar o arquivo dele sem mexer resulta em hook que roda, não dá erro nenhum e nunca injeta nada, que é o pior tipo de defeito: o silencioso. - Esforço
- Estimativa minha: 1 hora e meia. A maior parte é calibrar o corte de
score, porque a escala da nossa API não é a mesma dele, e conferir na tela que o bloco realmente chega.
- O que ele faz
- Antes de qualquer ferramenta rodar, checa duas famílias. Nas ferramentas de escrita, pega o caminho do arquivo e bloqueia se estiver fora das zonas permitidas. No Bash, procura padrão destrutivo por expressão regular:
rmcom caminho absoluto,mvolhando o destino, e redirecionamento com>ou>>pra fora das zonas. - A filosofia, que é a parte boa
- Leitura é sempre livre e a dúvida sempre libera. Caminho relativo, que não dá pra julgar, passa. Payload que não abre, passa. Lista de zonas vazia, passa. Ele prefere deixar escapar a travar a agente à toa, e diz isso no comentário do código. É a mesma escolha do nosso
security-scan.py. - As zonas
- Ficam num JSON editável em
state/trusted-folders.json, com padrões embutidos caso o arquivo não exista: o projeto, a pasta do bot,/tmpe a pasta de entregas. Quando bloqueia, a mensagem já ensina a saída: peça ao Chefe pra adicionar o diretório na lista. E oAGENTS.mdfecha o cerco com uma regra de comportamento: se bater no bloqueio, avise, nunca contorne por outro caminho. - Funciona aqui?
- Funciona, e o Claude Code ainda oferece mais. Além do
exit 2, o evento aceita uma resposta em JSON compermissionDecisionvalendodenye um campo de motivo, e até umupdatedInputque reescreve o comando antes de rodar. Dá pra fazer melhor que o original. - Isso conflita com o nosso security-scan?
- Não, os dois olham eixos diferentes e somam. O nosso tem modelo de ameaça: baixar código e canalizar pro interpretador, contêiner privilegiado, bomba de processos, formatar disco,
push --forceno main. O dele é territorial: onde pode escrever. Um pega o comando perigoso em qualquer lugar, o outro pega o comando comum no lugar errado. - Esforço
- Estimativa minha: 1 hora, quase toda gasta em desenhar a lista de zonas sem estrangular a operação. A gente escreve em muito lugar:
/opt/mari,/opt/mari-bot, projetos,/tmp, pastas de usuário. Lista apertada demais atrapalha mais do que ajuda.
- O que ele faz
- Quase nada, e ele sabe disso. O script existe pelo efeito colateral: apagar a trava de boot pra que o hook de prompt reinjete o contexto de início na próxima mensagem. Ele ainda escreve o contexto na saída, apostando que uma versão futura do Kimi passe a consumir, e comenta isso no código.
- Funciona aqui?
- O arquivo não, a função sim, e melhor. Como no Claude Code essa saída é injetada de verdade, o boot entra por aqui, direto, sem trava, sem lock e sem depender do primeiro prompt. O que a gente aproveita é o
naia_bootstrap.py, que é quem monta o conteúdo.
- O que ele monta
- Três blocos e nada mais: uma nota curta de quem ela é e onde responde, o
pending.mdcortado em 4.000 caracteres, e as 10 últimas mensagens dela puxadas do Postgres com filtro por identificador de agente. Cada mensagem cortada em 800 caracteres. - Por que isso importa mais que o código
- É a materialização do boot magro. O texto que ele injeta chega a dizer, dentro do próprio contexto: não releia arquivos grandes no boot, o grounding traz o que for relevante a cada mensagem. Compare com o nosso protocolo, que manda ler
SOUL.md,USER.md,decisions.md,projects.md,pending.mdeinfra.md, mais 30 trocas, antes de responder a primeira palavra. - Funciona aqui?
- O conteúdo sim, com adaptação: nosso banco é
mari_memory, a tabela não tem coluna de agente e a senha vem de variável de ambiente, não de cofre. A parte de trava de boot some inteira. - O que isso custa de decisão
- Trocar o protocolo de boot mexe no
CLAUDE.md, que é a alma dela. Isso é decisão do Chefe, não minha. Mas é onde mora o ganho maior: menos contexto na largada é mais tempo de trabalho antes do reinício.
- O que ele faz
- Antes de o contexto ser compactado, chama o consolidador de conversa uma vez, pra nada se perder na compactação.
- Funciona aqui?
- O evento existe, aceita filtro por manual ou automático, e
exit 2até bloqueia a compactação. Mas o valor pra nós é menor, e é honesto dizer: aqui quem grava o histórico é o bot, antes e depois do Telegram confirmar, então compactar não apaga conversa. O que se ganha é pequeno. - O que eu faria no lugar
- Usar o
SessionStartcom filtro decompactpra reinjetar o contexto essencial depois da compactação. É o mesmo problema atacado pelo outro lado, e resolve melhor: em vez de salvar antes de perder, restaurar depois de perder.
- O que ele faz
- Quatro linhas. Quando um turno morre por erro de API, grava a hora num arquivo e registra no log. Não bloqueia nada.
- Pra que serve
- Quem consome é o bot: com esse marcador, ele sabe distinguir turno que morreu de turno que está demorando, e cutuca em 60 segundos em vez de esperar 180. É pouco código pra uma diferença que o Chefe sente na hora.
- Funciona aqui?
- Sim, o evento
StopFailureexiste no Claude Code. Encaixa direto no nossocontingency.py, que hoje adivinha o estado lendo a tela do tmux atrás de padrões de apagão. - Esforço
- Estimativa minha: 20 minutos pro hook, mais o ajuste no bot pra ler o marcador.
Uma ideia que nem ele explorou
Hook por subagente, no cabeçalho do arquivo do agente
Descobri hoje conferindo a documentação: no Claude Code, cada subagente pode declarar hooks próprios no cabeçalho, que valem só enquanto ele estiver ativo, e depois são limpos sozinhos. Isso não existe no mundo do Kimi e o Denderson não usa.
O uso óbvio pra casa: um hook de Stop no jonathan-copy e em quem mais produz texto, que lê a fala final e bloqueia se encontrar travessão ou os vícios de IA que o humanizer-ptbr caça. Hoje a regra de zero travessão é a instrução mais repetida da casa e a que mais fura. Vira impossível de furar, e custa uns 40 minutos.
Checkpoint, consolidação e vigia
A resposta que o Chefe não vai gostar, e é a verdade
O checkpoint dele não faz a Mari cair e outra continuar
Essa era a esperança, e eu preciso derrubar antes de descrever o resto. O restore-checkpoint.sh diz, em comentário, na saída do comando e no README, três vezes: o registro da sessão não é restaurado. A sessão do CLI não é regravável, então a cópia do registro fica lá só pra auditoria e leitura humana.
O que ele restaura são arquivos: as anotações de memória, o marcador de tarefa ativa e o arquivo de alma. Continuidade de conversa, não.
Ou seja: ele chegou exatamente na mesma conclusão que a gente chegou hoje de manhã. O histórico mora fora da sessão, no banco, gravado por quem não morre junto. A intenção mora numa ficha. A sessão em si é descartável. A gente não estava errado, e não existe truque escondido no pacote dele.
checkpoint.py, o que ele realmente faz
- De 30 em 30 minutos, copia para uma pasta com carimbo de hora: o registro bruto de todas as sessões vivas, todos os
memory/*.md, o marcador de tarefa e oAGENTS.md. - Escreve um
manifest.jsoncom sha256, tamanho e caminho de origem de cada arquivo. Dá pra provar depois que o conteúdo é o mesmo. - Retenção de 24 checkpoints, que dá 12 horas de janela. O excedente é apagado por ordem de nome.
- Falha por arquivo não derruba a rodada: erro é registrado e a cópia continua.
O que isso melhora aqui: nosso backup roda uma vez por dia, às 6h, e o git guarda o histórico. Entre um backup e o outro existe uma janela de 24 horas. Se uma consolidação noturna reescrever um arquivo de memória errado, a versão boa está a até um dia de distância. Trinta minutos de granularidade fecha isso por quase nada.
consolidate-kimi-conversations.py
É o maior arquivo do pacote e o mais bem feito de engenharia. Ele lê o registro bruto da sessão, que é um arquivo de uma linha JSON por evento, e transforma em linhas de conversa no Postgres.
- Marcação por byte. Guarda, por arquivo, em que byte parou. Na próxima rodada lê só o que cresceu. Nada de reprocessar o arquivo inteiro a cada 2 minutos.
- Linha incompleta ele segura. Se o último pedaço lido não termina em quebra de linha, quer dizer que o CLI está escrevendo naquele instante. Ele recua até a última quebra e deixa o resto pra próxima. É o cuidado que separa código que funciona de código que funciona sempre.
- Agrupa fala por turno e passo, então várias partes de texto do mesmo passo viram uma única fala em vez de dez linhas picadas.
- Filtra origem. Só entra mensagem cuja origem é o usuário de verdade. Injeção de hook, gatilho de sistema e tarefa ficam de fora, senão o contexto injetado voltaria pro banco como se fosse fala do Chefe.
- Chamada e resultado de ferramenta viram linha resumida, com o resultado cortado em 200 caracteres.
- Grava com
ON CONFLICT DO NOTHING, então rodar duas vezes não duplica.
Aqui ele tem uma coisa que a gente não tem
A nossa memória de conversa é o que passou pelo Telegram, porque quem grava é o bot. A dele é a sessão inteira: cada ferramenta chamada, cada fala interna, cada resultado. Quando ele pergunta "o que a gente fez ontem", a busca alcança o trabalho, não só o papo.
É um ganho real. Também é um custo real, em volume de banco e em ruído na busca vetorial. Eu não recomendaria copiar isso agora, mas registro como diferença honesta e como opção pra quando a memória de trabalho fizer falta.
task-watchdog.py, contra o nosso
| Comportamento | Naia Kimi | Nosso |
|---|---|---|
| Como sabe que há trabalho | Lê state/active-task.json, que a agente escreve à mão ao delegar | Descobre sozinho: varre os arquivos de subagente do transcrito e considera ativo o que mexeu nos últimos 3 min |
| Sinal de vida pro Chefe | Sim, a cada 7 min, escreve direto no outbox | Não manda sozinho: cutuca a Mari pra que ela escreva o status |
| Cutucão na agente | Se ociosa há 5 min com tarefa aberta. Uma vez só por marcador | Se o Chefe está há 10 min sem notícia e há subagente vivo. Espera de 10 min entre cutucões |
| Marcador órfão | Cala a boca depois de 3 h e registra no log | Não se aplica, não existe marcador |
| Progresso real | Sim, cola a última linha de progresso do trabalhador na mensagem | Não. Manda o nome do subagente e há quantos minutos |
| Truque de teclado | send-keys e depois Enter, porque a interface do Kimi ignora C-m | C-m e ainda KPEnter de reforço, porque a nossa interface ociosa já ignorou C-m |
Placar honesto: o nosso é melhor no que mais importa, que é descobrir trabalho sem depender da memória da agente. Marcador escrito à mão é exatamente o tipo de coisa que se esquece justo no dia corrido.
Ele ganha em dois pontos: manda sinal de vida sozinho, mesmo com a agente travada, e mostra progresso de verdade. E tem uma frase no AGENTS.md que eu levaria pro nosso arquivo sem mudar uma vírgula: o cutucão é pedido de verificação, não de entrega. Se o subagente ainda roda, ignore e continue esperando.
Qual provedor ele usa de verdade
A hipótese do OpenRouter não se confirma
Não é OpenRouter, e ele diz isso com todas as letras
O AGENTS.md tem uma seção só sobre isso: meu cérebro é e continua 100% K3. Nunca troco meu provider nem meu modelo. O openrouter.sh é bancada de teste sob demanda, pra quando o Chefe dele pedir "testa essa tarefa no modelo X" ou "compara os modelos". Tem três comandos: listar com contexto e preço, rodar um prompt, e comparar o mesmo prompt em vários modelos com tabela de latência, tamanho e custo.
Ele ainda amarra o uso: modelo pago só roda com pedido explícito, e o custo de cada teste é reportado na entrega. Prefere os gratuitos pra exploração.
O que ele usa mesmo, e por que isso mexe com a nossa conta
Ele roda o Kimi Code CLI, um binário próprio, com o modelo kimi-code/k3. E o README e o AGENTS.md repetem a mesma expressão em três lugares: assinatura, sem custo de API.
Isso importa porque o nosso plano de hoje desenhou outro caminho: manter o Claude Code e apontar o endereço da API pro endpoint compatível da Moonshot, com crédito pré-pago de US$ 20 e estimativa de US$ 15 a US$ 120 por mês conforme o uso.
| Eixo | Caminho do Denderson | Caminho do nosso plano |
|---|---|---|
| Programa | Kimi Code CLI, binário próprio | Claude Code, o mesmo de hoje |
| Modelo | kimi-code/k3 | kimi-k3 por variável de ambiente |
| Como paga | Assinatura. Custo marginal por mensagem: zero | Crédito pré-pago por token |
| Nossas skills | Não carregam. Formato e motor são outros | Carregam iguais. A casca é a mesma |
| Nossos hooks | Outro motor de hook, outro protocolo de saída | Os mesmos, sem reescrever |
| Nossos 7 subagentes | Reescrever | Copiar com o modelo trocado |
| Prova de que funciona | Roda em produção, todo dia, na casa dele | Endpoint testado daqui, agente ainda não montado |
O que eu recomendo perguntar pra ele, em uma frase
Qual plano de assinatura ele paga e se esse plano aguenta o volume de uma agente trabalhando o dia inteiro. Nosso plano registrou que não achou tabela oficial confiável dos planos e classificou como suposição de fonte contraditória. O Denderson é a fonte que faltava, e é uma pergunta só.
O que eu não afirmo, porque não dá pra saber pelo pacote: se a assinatura do Kimi Code serve também pro endereço compatível que o nosso plano usa. São dois produtos com endereços diferentes. Isso muda tudo na conta e ninguém deveria chutar.
Como o negócio sobe, e como fica no Linux
A cadeia dele tem quatro elos: o serviço do macOS chama o supervise.sh, que cria a sessão tmux rodando o start.sh, que roda o CLI em laço. Se o CLI morre, o laço reergue. Se a sessão morre, o serviço reergue.
O start.sh é a peça com miolo, e a lógica é boa: continua a sessão anterior por padrão, começa do zero se não houver nenhuma, espera de 5 a 15 segundos entre tentativas, e se levar 3 quedas em menos de 120 segundos a próxima vai do zero, partindo do princípio de que a sessão anterior está corrompida. Sessão que durou mais de 5 minutos zera o contador.
Tem até uma nota amarga no comentário: o modo sem confirmação não combina com continuar sessão, então ele configura isso no arquivo do CLI e não na linha de comando. É o tipo de coisa que só se aprende quebrando.
A tradução, elo por elo
| No mundo dele | o que faz | Aqui |
|---|---|---|
| com.naia.kimi-agent | mantém a agente viva | systemd com Restart=always. Já temos, é o mari.service |
| com.naia.kimi-telegram-bot | mantém o bot vivo | systemd. Já temos |
| com.naia.kimi-consolidate | de 2 em 2 min | Uma linha de crontab. Não precisamos: o bot já grava |
| com.naia.kimi-task-watchdog | de 1 em 1 min | Uma linha de crontab. Já temos, é o status-nudge.sh |
| com.naia.kimi-checkpoint | de 30 em 30 min | Uma linha de crontab. Falta, e é o que interessa trazer |
| supervise.sh | cria e vigia o tmux | O systemd faz isso. Script desnecessário |
| start.sh | laço com recomeço | Já temos equivalente |
| install.sh | instalador idempotente | Reescrever do zero se quisermos um |
Esforço de portar a camada de subida
Perto de zero, porque a gente já está do outro lado. Não é que a camada dele seja difícil de traduzir, é que ela resolve um problema que a nossa infraestrutura já resolve há meses, e com ferramenta melhor: systemd tem reinício, dependência entre serviços, registro unificado e estado consultável. O launchd dele ainda precisa de um supervise.sh no meio pra dar conta do tmux.
O único item que falta mesmo é uma linha de crontab pro checkpoint de 30 minutos. É isso.
Uma coisa do install.sh que eu levaria
Ele é tolerante de propósito: se o token do bot ainda não existe no cofre, o instalador sobe a agente e deixa o bot desligado, avisando no resumo o que ficou pendente. Não quebra, não sobe pela metade em silêncio, e diz na cara o que falta. É o desenho certo pra quando a gente for subir a Mari Kimi e o token do bot novo ainda não estiver na mão.
Ele tem, nós temos
O que ele tem e nós não
- Hook de Stop que bloqueia promessa vazia. A trava que a gente mais precisa e não tem.
- Busca de memória automática por mensagem. Temos as peças, nunca ligamos.
- Boot magro como doutrina. Não é script, é decisão de arquitetura, e é a mais barata de copiar.
- Lista de pastas com permissão de escrita. Contenção territorial, que o nosso
security-scannão faz. - Checkpoint de 30 minutos com prova de integridade. Nosso backup é diário.
- Memória de sessão inteira, com ferramenta chamada e fala interna, não só o que passou pelo Telegram.
- Progresso de verdade no sinal de vida, vindo do fluxo do trabalhador, não da percepção da agente.
- Marcador de turno morto, que separa travou de está demorando.
- Segredo só do cofre em tempo de execução. Zero credencial em disco.
- Regra escrita de irmã, com fronteira explícita entre as duas agentes.
O que nós temos e ele não
- Modelo de ameaça de verdade no hook de comando. O nosso
security-scan.pypega código remoto canalizado pro interpretador, contêiner privilegiado, montagem da raiz do host, bomba de processos, formatação de disco epush --forceno main. O dele checa caminho. Ele bloquearia umrmno lugar errado, mas deixaria passar umcurlcanalizado probash. - Rastreio automático de subagente. O
task-tracker.pyescuta três eventos e mantém sozinho quem está rodando, o que está fazendo e o que terminou, com trava de arquivo pra escrita concorrente. Ele escreve marcador à mão. - Vigia de contexto com medição real. Lê os tokens do transcrito, detecta sessão travada por erro de API e manda reiniciar aos 75%. No pacote dele não existe nada que meça contexto. A janela de 1 milhão do K3 adia esse problema, não elimina.
- Setenta e duas rotinas de relógio contra quatro serviços dele. Briefing, lembretes três vezes ao dia, monitor de e-mail, de chip, de rastreio, de saturação do banco, backup e curador de skills.
- Skills de processo e curador semanal. Nada equivalente no pacote.
- Busca vetorial sobre conhecimento e memória em arquivo, com 3.858 pedaços indexados de hora em hora. A dele indexa a tabela de conversa.
- Ficha por fato já rodando, com 185 arquivos na automemória. É o padrão que o plano da Mari Kimi quer estender pro resto.
- Ferramentas externas conectadas: agenda, e-mail e drive.
- Cópia de segurança em git, diária e automática, com histórico versionado.
Leitura de conjunto: a gente está claramente na frente em operação, quer dizer, na máquina que mantém tudo de pé, mede, avisa e cuida do negócio. Ele está na frente em disciplina da agente, quer dizer, nas travas que impedem a agente de se comportar mal. São dois eixos diferentes, e por isso o pacote é útil: ele não repete o que a gente tem, ele preenche.
O que muda no plano da Mari Kimi
Li a página do plano inteira antes de escrever isto. Quatro coisas mudam, uma se confirma e uma pergunta nova aparece.
Muda · a decisão de provedor ganhou um dado novo
O plano tratou a assinatura como caminho possível, com a ressalva honesta de que não achou tabela oficial e que as fontes se contradiziam. Agora existe um terceiro rodando assinatura em produção, todo dia. Isso não decide sozinho, porque o caminho dele custa a nossa casca inteira: skills, hooks e os sete subagentes não atravessam pro Kimi Code CLI.
Mas muda a ordem das coisas. Antes de recarregar crédito, vale uma pergunta ao Denderson. Se a assinatura aguentar o volume, a conta de US$ 15 a US$ 120 por mês do plano some, e a discussão passa a ser sobre perder ou não a casca.
Muda · o relógio ganha uma quarta camada, e ela é estrutural
O plano tem três camadas pra impedir a Mari K de disparar rotina de horário: usuário separado, outbox separado e um delta de instrução. E o próprio plano admite o ponto fraco: a camada 3 é instrução, e instrução é exatamente onde o Kimi é pior.
O trusted-folders é a quarta camada, e não é instrução. Um hook de PreToolUse no settings.json dela, com /opt/mari-bot/outbox fora das zonas permitidas, transforma "ela não deve escrever no outbox da outra" numa recusa da máquina. Custa quase nada e vale mais que qualquer parágrafo no arquivo de instrução dela.
Digo com todas as letras: isso é melhor do que a gente desenhou. É a implementação em máquina da regra de irmã que ele escreveu em prosa, e resolve justo a camada que o plano identificou como a mais frágil.
Muda · o travessão deixa de ser esperança
O plano lista, entre os pontos piores do Kimi, que a regra de zero travessão vai furar e que o humanizer-ptbr deixa de ser acabamento e vira passe obrigatório. Passe obrigatório é instrução, e instrução é onde ele falha.
Com hook de Stop lendo a última fala e recusando o encerramento quando encontra travessão, a regra passa a se cumprir sozinha, nela e nos subagentes dela. Vira o item mais barato de todo o plano com efeito direto no que o Chefe lê.
Muda · o marcador de tarefa atravessa a queda
O plano reconhece o furo: o que ela pensou e não publicou morre junto, e arquivo escrito pela metade fica pela metade. A resposta dele era a ficha de pendência guardar a intenção.
O active-task.json é essa ficha, em formato que máquina lê. Se a Mari cair no meio, o arquivo sobrevive, e a Mari K consegue ler o que estava em andamento, com qual subagente e desde quando. Não devolve a conversa, mas devolve o rumo. Encaixa direto na tese de duas agentes com a mesma pasta.
Se confirma · a memória não volta, e não é limitação nossa
O plano concluiu que o histórico mora no banco, gravado pelo bot, e que a sessão é descartável. O pacote dele chegou na mesma conclusão por um caminho independente, e assumiu isso por escrito no script de restauração. Ninguém resolveu o problema porque não tem como resolver desse lado. Vale como confirmação, não como novidade.
Aparece · um detalhe pequeno que eu adotaria
Ele separa as irmãs por identificador de agente na mesma tabela, e não por chave de sessão. O nosso plano usa session_key='telegram-dm-k'. Os dois funcionam, mas o dele responde melhor a "o que a outra estava fazendo", que é justamente a consulta que o plano quer que as duas façam antes de começar tarefa longa. Carimbar as duas coisas custa uma coluna e resolve.
O que não muda: usuário Unix separado, mesma pasta de trabalho, bot próprio, memória compartilhada, correção do pgrep no vigia e vigia de contexto próprio pra ela. Nada no pacote dele contradiz nenhuma dessas escolhas. Aliás, ele confirma a primeira: o AGENTS.md dedica uma seção inteira à separação entre as irmãs, e ele fez isso sem usuário separado, só na base da instrução. Foi a saída que o macOS deixou. A gente tem uma melhor.
Implantação, por valor sobre esforço
Os tempos são estimativa minha, não medição
Os sete primeiros não dependem de Kimi nenhum e melhoram a operação de hoje. Do oitavo em diante só faz sentido junto com a Mari Kimi.
Hook anti-promessa no Stop
Lê o last_assistant_message, procura promessa futura em português, confere se houve ferramenta no turno por um carimbo de PostToolUse, e recusa o encerramento quando não houve. Trava de repetição obrigatória, porque aqui não existe proteção nativa contra laço. Vale pra Mari e pra todos os subagentes.
Hook de travessão e vício de IA
Mesma família do primeiro, e é o mais barato com efeito visível. Bloqueia o encerramento quando a fala final tem travessão ou os vícios que o humanizer-ptbr caça. Começa pela Mari e pelo jonathan-copy, que é quem mais produz texto.
Busca automática no UserPromptSubmit
Toda mensagem do Chefe passa pela 3007 antes do modelo, e o que vier relevante entra junto, com a instrução de não contradizer. Exige reescrever o leitor de resposta, porque a nossa API devolve results com score e a dele devolve chunks e facts com similarity. Copiar sem mexer dá hook que roda e nunca injeta nada.
Marcador de turno morto
Quatro linhas no StopFailure gravando a hora, e o contingency.py passando a ler esse marcador em vez de adivinhar pela tela do tmux. O Chefe sente na hora: recuperação em 60 segundos em vez de 180.
Checkpoint de 30 minutos com manifesto
Uma linha de crontab e um script curto: cópia das fichas de memória e do estado, com sha256 e retenção de 24. Fecha a janela de 24 horas entre um backup e o outro. Não mexe em comportamento nenhum, só acrescenta rede de proteção.
Lista de pastas com permissão de escrita
Hook de PreToolUse ao lado do security-scan.py, com falha aberta e leitura sempre livre. O trabalho não é o código, é desenhar a lista sem estrangular a operação, porque a gente escreve em muito lugar. Fica melhor que o dele usando permissionDecision com motivo em vez de exit 2 seco.
Boot magro
O de maior ganho e o de maior consequência. Passa o protocolo de boot pra um hook de SessionStart, com filtro por motivo, e corta a leitura proativa dos seis arquivos, confiando na busca do item 3. Menos contexto na largada é mais tempo de trabalho antes do reinício aos 75%. Mexe no CLAUDE.md, que é a alma dela, e por isso é decisão do Chefe. Só depois do item 3 estar funcionando, nunca antes.
Progresso real no status de 10 minutos
Trabalhador em segundo plano com saída em fluxo JSON, escrevendo um arquivo de progresso legível que o status-nudge.sh cola na mensagem. Hoje o status diz o que a agente acha que está acontecendo. Passa a dizer qual ferramenta rodou por último e quando.
Pergunta ao Denderson sobre a assinatura
Qual plano ele paga e se aguenta uma agente trabalhando o dia inteiro. Custa uma mensagem e pode apagar a linha de custo mensal do plano da Mari Kimi. Ação para fora, então é ele quem manda.
Zonas de escrita como quarta camada do relógio
No settings.json da Mari K, com o outbox da Mari fora da lista. Transforma a fronteira entre as irmãs em recusa de máquina, e não em parágrafo que ela pode não seguir.
Marcador de tarefa compartilhado
Um arquivo de estado que as duas escrevem e leem, pra que a queda de uma no meio do trabalho seja visível pra outra. Junto com o carimbo de identificador de agente na tabela de conversa.
Hooks no cabeçalho de cada subagente dela
Os sete agentes dela já vão ser copiados com o modelo trocado. Aproveitar a passagem pra prender a trava de travessão no cabeçalho de quem escreve texto. Custo marginal perto de zero, feito na mesma hora.
Se ele só liberar uma coisa
Os itens 1 e 2, juntos. São duas horas, mexem no mesmo lugar, atacam os dois defeitos que mais se repetem aqui, e nenhum dos dois depende de Kimi, de dinheiro ou de root. O item 5 pode ir junto porque não precisa de autorização: só acrescenta cópia de segurança e não muda comportamento nenhum.