Por que a memória Honcho do Hermes cai em outro projeto

Você mantém três repositórios: um monorepo de backend, um frontend e uma pasta de scripts de operação que não abre há dois anos. Semana passada você reescreveu o fluxo de estorno no monorepo. Hoje abre o Hermes dentro da pasta de scripts, pergunta como ficou aquele refactor — e recebe uma resposta detalhada e confiante sobre um projeto que nunca foi assunto naquele diretório. O inverso assusta mais: um repositório recém-clonado, sem uma linha escrita, e a primeira conversa já “lembra” do seu diretório home inteiro. Você roda o Hermes por projeto exatamente para que cada repositório tenha a própria memória. O Honcho estava arquivando em outro lugar.
Os prompts estavam certos. O destino não estava. O PR #112602 (mesclado em 2026-09-16) corrige os dois caminhos de código que roteavam as sessões para o lugar errado. A seguir: o que quebrou, como configurar a sessionStrategy para ela realmente valer e como descobrir em qual sessão sua memória caiu.
Dois termos, uma frase cada
Honcho é um dos memory providers do Hermes: ele modela você como um perfil de usuário que persiste entre sessões — user representation, peer card — em vez de tratar cada conversa como uma ilha.
sessionStrategy define como as sessões do Honcho (pense em “baldes de memória”) se mapeiam no seu trabalho. O padrão é per-directory, com quatro níveis:
| sessionStrategy | O que faz |
|---|---|
per-session |
Cada execução do hermes ganha uma sessão nova; a memória volta pelas ferramentas. A documentação recomenda isso para quem está começando |
per-directory |
Uma sessão por diretório de trabalho; o contexto se acumula entre execuções (padrão) |
per-repo |
Uma sessão por repositório git |
global |
Uma única sessão para todos os diretórios |
Um detalhe que vale manter separado: no modo hybrid / context, o Honcho injeta duas camadas de contexto no system prompt a cada turno — uma camada base (resumo da sessão, user representation, peer card, autorrepresentação da IA) e um suplemento dialético gerado por LLM. contextCadence tem padrão 1 e dialecticCadence tem padrão 2 (a documentação sugere 1–5); o resultado concatenado é truncado pelo orçamento de contextTokens. Memória de sessão e perfil de usuário são coisas diferentes — misturar as duas torna esses bugs de roteamento muito mais difíceis de diagnosticar.
Se você prefere manter a memória no próprio disco, o caminho com LanceDB está em como o Hermes implementa memória semântica: mesmo problema, outro armazenamento.
O sintoma: três repositórios, uma memória só
Três variações da mesma queixa, todas com a mesma configuração:
- Você pergunta sobre o repositório B estando no A, e o Honcho traz os detalhes de B.
- Um repositório novo já chega carregado de preferências e conclusões de projetos antigos.
- A configuração diz
per-repo, mas o agente se comporta comoglobal.
As três significam a mesma coisa: todos os projetos caem na mesma sessão, e essa sessão não tem o nome de nenhum dos seus repositórios.
Causa um: um título automático renomeia a sessão
O Hermes escreve títulos de conversa para você. A proveniência deles é derived ou llm (o mesmo vale para os títulos de lineage dos branches do Desktop). Eles existem para você reconhecer conversas numa lista: são metadados de exibição.
O código antigo discordava. Bastava a sessão ter um título para ele entrar na derivação do nome da sessão. Então um título automático como “Refactor the billing module” arrancava a conversa da sessão do repositório e a renomeava para Refactor-the-billing-module. Sua sessionStrategy continuava configurada; só não era consultada.
Causa dois: a resolução usava o diretório de inicialização, não o workspace
Essa é mais silenciosa. A resolução da session key rodava contra os.getcwd(), o diretório de inicialização do processo backend. No Desktop ou num messaging gateway, esse processo costuma iniciar em $HOME; então, não importava de qual repositório você escrevesse, o workspace que o Honcho enxergava era o seu diretório home. Todos os projetos colapsavam num único balde chamado home.
É por isso que tantas configurações pareciam global enquanto o arquivo de configuração dizia outra coisa.
O que o PR #112602 mudou
A correção é deliberadamente estreita: uma mudança por bug, ambas no caminho de resolução.
plugins/memory/honcho/client.py::_resolve_session_name_base— um título com proveniênciaderivedoullmé metadado de exibição e pula o branch do título por completo. Um/titleexplícito (proveniênciauser) continua vencendo. Um título sem origem registrada mantém o comportamento antigo.plugins/memory/honcho/__init__.py::_resolve_session_key— resolve contrakwargs["cwd"]ouresolve_agent_cwd()(pinned session cwd →TERMINAL_CWD→ diretório de inicialização): o mesmo contrato que o system prompt e a descoberta de arquivos de contexto já usavam, em vez deos.getcwd().hermes_state_common.py— as constantesTITLE_SOURCE_*ficam no módulo irmão cycle-safe, entãoSessionDBe o plugin leem o mesmo vocabulário.agent/agent_init.py—AIAgent(cwd=...)gravaagent.session_cwd, e_memory_provider_init_kwargspassasession_title_sourceecwdpara o provider.tui_gateway/*— os caminhos de eager-resume, branch, compute-host e deferred build passamcwd_override(o registro de sessão deles ainda não está em_sessions, então_set_session_contextapagaria o cwd). Um workspace que muda durante um deferred build também chega ao agente já construído.
Vale memorizar a precedência. Messaging gateways mantêm a session key estável por chat, independentemente de estratégia ou título. Para as outras sessões: identidade per-session → mapeamento manual de diretório → título explícito → a estratégia configurada, e vence o primeiro que casar.
A mesclagem foi validada de ponta a ponta com imports reais (um SessionDB real, um HonchoMemoryProvider real, cwd do processo apontando para um $HOME falso, cwd da sessão amarrado a um repositório git chamado myapp, estratégia per-repo):
| Caso | Antes | Depois |
|---|---|---|
| sem título | home |
myapp |
| título LLM “Refactor the billing module” | Refactor-the-billing-module |
myapp |
/title My Explicit Name explícito |
My-Explicit-Name |
My-Explicit-Name |
A terceira linha é intencional: um título escrito por você continua sendo uma sobreposição.
Configurar a sessionStrategy corretamente
O assistente continua sendo o caminho com menos erro — ele pergunta sobre o gateway e escreve as chaves por você:
hermes memory setup # escolha "honcho" na lista de providers
Configurando à mão, o config.yaml precisa no mínimo disto:
# ~/.hermes/config.yaml
memory:
provider: honcho
echo 'HONCHO_API_KEY=***' >> ~/.hermes/.env
A API key sai em honcho.dev. Configurações como sessionStrategy ficam no bloco de settings do provider Honcho — o mesmo que o hermes memory setup escreve. Se preferir não editar à mão, aperte [e] no prompt do assistente e defina as chaves no editor dele.
Como escolher sua estratégia:
per-repo— um monorepo mais vários repositórios independentes, cada um com memória própria. É o cenário lá de cima.per-directory— você trabalha num diretório a longo prazo e não precisa isolar subpastas. Não é à toa que é o padrão.per-session— cada execução começa limpa e a memória é buscada por ferramentas. É também o melhor ajuste para depurar, porque separa com mais nitidez memória de sessão e perfil de usuário.global— só quando você realmente quer um perfil compartilhado entre todos os projetos.
Os demais ajustes (em boa parte ligados a gateway, listados juntos para referência):
| Ajuste | Padrão | O que faz |
|---|---|---|
sessionStrategy |
per-directory |
Os quatro níveis acima; estratégias por diretório e mapeamentos manuais de sessions usam o diretório de trabalho lógico da sessão, não o diretório de inicialização do backend |
a2aSessions |
true |
DMs de outros bots são gravadas na própria sessão por bot remetente, nunca na do humano; false ignora totalmente turnos escritos por bots |
pinUserPeer |
false |
Só gateway. Quando true, todo usuário da plataforma colapsa em peerName |
userPeerAliases |
{} |
Só gateway. Mapa de IDs de runtime para peers ({"7654321": "alice"}), muitos para um |
runtimePeerPrefix |
"" |
Só gateway. Dá namespace a IDs de runtime desconhecidos (telegram_7654321) quando nenhum alias casa |
A resolução de peers no gateway testa de cima para baixo, e o primeiro que casar vence: pinUserPeer → userPeerAliases[id] → runtimePeerPrefix + id → ID de runtime cru → peerName → fallback da session key. Se fatos de outra pessoa estão grudando em você no Telegram ou no Discord, é essa tabela que você precisa ler. Um aviso da documentação: virar pinUserPeer de true para false não migra dados — o histórico sob peerName fica onde está. Os detalhes de comando estão na referência do hermes memory.
Como saber em qual sessão a memória caiu
Depois da correção o nome da sessão é derivável, então você pode raciocinar de trás para frente:
- Olhe o formato do nome. Em estratégias por diretório ou repositório ele é o nome do diretório ou do repositório (na validação,
myapp); sem informação de workspace, é o diretório de inicialização (home); um título explícito vira a forma com hífens (My-Explicit-Name). Um nome de sessão com formato de título é a assinatura do bug antigo: se você vêRefactor-the-billing-module, o título tomou conta da sessão. - Faça um teste cruzado. No repositório A, peça ao agente para registrar uma conclusão que só pertence a A (uma conclusão escrita por
honcho_conclude, ou o user card por trás dohoncho_profile). Depois vá para o repositório B e pergunte sobre ela. Comper-repo, B deve ficar em branco. Se ele responder, os dois repositórios falam com a mesma sessão. - Confirme o próprio diretório de trabalho. Peça ao agente o diretório de trabalho da execução atual e compare com o repositório onde você acha que está. A cadeia é contexto de sessão →
terminal.cwdcom escopo → diretório de inicialização; se algum elo devolver$HOME, sua memória vai para o baldehome. - Use
per-sessioncomo controle. Troque temporariamente para ele: toda sessão nova deve começar vazia. Se fatos ainda aparecerem, eles vêm do perfil de usuário (user representation / peer card), não da memória de sessão — isso é intencional, não contaminação cruzada.
Sessões criadas antes da correção
A documentação é direta: sessões criadas antes de a proveniência do título ser registrada mantêm o comportamento antigo. Um título automático antigo não se distingue de um título de usuário antigo, então um título sem origem é tratado como sobreposição explícita. Atualizar não devolve essas sessões para a sessão do repositório.
Existe um segundo limite: esta mudança não migra mensagens já gravadas em sessões roteadas errado. O histórico que caiu no balde errado fica lá. Na prática: abra uma sessão nova no diretório correto — ela resolve para o nome de sessão certo — e continue usando /title explícito quando quiser uma sobreposição deliberada, porque esse comportamento não foi mexido.
Se você gosta de transformar conclusões em notas duráveis, fazer o agente escrever no seu vault do Obsidian estando no diretório de projeto certo contorna o problema inteiro: o sistema de arquivos já é particionado por projeto. E se você agenda trabalho, a mesma cadeia de memória aparece em cron e continuidade de memória.
Resumo
- A memória do Honcho tem um destino definido, e esse destino é decidido pela
sessionStrategymais o diretório de trabalho da sessão. - Dois bugs quebraram uma metade cada: títulos automáticos (
derived/llm) não anulam mais a estratégia, e a resolução agora segue o workspace da conversa em vez do diretório de inicialização do processo. Um/titleexplícito continua sendo sua sobreposição manual. - Use
per-repoentre vários repositórios,per-directorypara trabalho longo num diretório só eper-sessionpara começar limpo ou caçar um bug de roteamento. - Para diagnosticar, leia primeiro o formato do nome da sessão e depois faça uma pergunta cruzando repositórios.
- Sessões antigas e histórico já mal roteado não se movem sozinhos: comece sessões novas a partir do diretório correto.