Por que sua exportação /save estava perdendo turnos — e o limite por trás disso


Você tem uma sessão de longa duração, a compactação já arquivou várias rodadas dela, e você quer exportá-la para guardar. /save json pela CLI funciona bem. Você faz a mesma coisa no aplicativo de desktop, importa o resultado depois e percebe que faltam pedaços — os turnos que a compactação arquivou simplesmente sumiram. Você começa a se perguntar se lembrou errado, ou se algum /undo os apagou.

Dois problemas estão empilhados aqui: o mesmo /save passava por três caminhos de código diferentes (CLI, plataformas de mensagens e o RPC session.save do desktop/TUI), e as duas regras que importam — o que exportar e a partir de que tamanho é grande demais — acabaram discordando entre si. A correção converge tudo para um único caminho.

Três superfícies de /save, antes três respostas

/save oferece suporte a json (um snapshot completo), md (uma transcrição legível) e html (uma página independente e compartilhável). O desktop e o TUI chamam o RPC session.save, enquanto a CLI e as plataformas de mensagens usavam outra implementação. A diferença aparecia na projeção da exportação:

  • Uma transcrição (md / html) é «o que o usuário viu», então precisa incluir os turnos que a compactação no local arquivou;
  • Um snapshot JSON é «o que uma importação restaura», então carrega todas as linhas armazenadas com seus sinalizadores active/compacted. Exportar apenas as linhas ativas descarta cada trecho arquivado da conversa; exportar os sinalizadores permite que a importação os restaure como arquivados em vez de reproduzi-los como contexto ativo.

O caminho do RPC não seguia essa regra de forma consistente, então uma mesma sessão era exportada de maneira diferente dependendo da superfície usada.

A regra agora: um caminho compartilhado pelas três

A correção extrai a lógica já correta da CLI para uma função compartilhada load_save_snapshot(db, session_id, fmt) em hermes_cli/session_export.py, e as três superfícies a chamam. Ela faz três coisas:

  1. Escolhe a projeção pelo formato: include_compacted=True para transcrições, include_inactive=True para JSON;
  2. Para um snapshot JSON, descarta as linhas desfeitas: só sobrevivem as linhas active ou compacted, enquanto as linhas removidas por /undo, rewind ou edição ficam de fora — de propósito, já que uma importação não deve ressuscitar o que o usuário desfez;
  3. Aplica o limite de exportação (abaixo).

Duas coisas menores vieram de brinde. O snapshot JSON agora usa a forma canônica de exportação, igual à da CLI, em vez das chaves legadas do aplicativo de desktop — e quando uma sessão não tem linha armazenada (ou a leitura falha), o fallback em memória grava essa mesma forma (id, model, started_at, system_prompt, messages). Ele também lê a linha que o agente ativo está escrevendo naquele momento, de modo que uma rotação de compressão não exporta um pai desatualizado.

O limite de exportação: sessions.max_export_messages

Materializar de uma vez na memória uma sessão de 100 mil linhas pode esgotar o processo, então existe uma proteção:

sessions:
  max_export_messages: 20000   # o padrão; 0 desativa a proteção

Ela é verificada por sessão, então um backup do banco inteiro com muitas sessões pequenas não é afetado — só a sessão grande demais é recusada. Depois desta correção, o /save json do desktop/TUI também a respeita, e:

  • a recusa chega como código de erro RPC 4131 (não o genérico 5011), com a mesma mensagem que a CLI imprime;
  • em sessões isoladas por turno (compute-host), o host repassa o código de erro do handler em control.error e o pai o transmite, de modo que sessões executadas remotamente informam o motivo real;
  • a mensagem orienta você a definir sessions.max_export_messages: 0 em config.yaml para desativar a proteção.

O último detalhe é fácil de passar batido e é importante: o limite é lido da configuração do perfil ao qual a sessão pertence. O aplicativo de desktop agora lê o state.db próprio desse perfil sob a configuração desse perfil, então o limite de exportação de um perfil secundário é o dele, não o do seu perfil principal.

Escolher por cenário

Com as regras claras, a escolha do formato é simples:

Seu objetivo Use Por quê
Arquivar / mover para outra máquina /save json Snapshot completo com sinalizadores de arquivamento; reimportável como está
Enviar para um colega /save html ou /save md Transcrição legível, com os turnos arquivados pela compactação incluídos
Sessão grande demais, exportação recusada hermes sessions export para uma sessão, ou aumentar/desativar max_export_messages O limite é avaliado por sessão
Publicar em algum lugar público Qualquer formato mais redact Limpa chaves de API, tokens e credenciais antes de gravar

A conclusão que vale guardar: «exportar» atende a dois propósitos diferentes e as regras diferem de propósito. Uma transcrição é para humanos, então fica o mais perto possível do que você viu — turnos arquivados incluídos. O JSON é para máquinas, então preserva os sinalizadores e remove as linhas desfeitas. Se você presumiu que a falta no JSON significava perda de dados, esta regra explica o caso.

Quando você pode obtê-la

A correção foi incorporada ao branch main em 3 de outubro de 2026. A versão mais recente é a v0.21.5 (tag v2026.9.24), então no momento ela vive apenas no main — embora sessions.max_export_messages em si já exista nas versões publicadas e funcione hoje. As instalações que acompanham o main ficam consistentes no próximo hermes update.

Para uma visão mais ampla, o guia de salvamento e exportação de sessão compara /save com hermes sessions export, e o guia de limites de sessão explica como as sessões são delimitadas. Se o que lhe interessa é o próprio mecanismo de compressão, integridade da compactação em meio ao turno e o artigo sobre a compactação em uma única chamada vão mais fundo. Os detalhes no nível do comando estão na referência do comando sessions.

A versão curta

O valor aqui não é um recurso novo, mas a eliminação da ambiguidade: antes você não conseguia saber se uma exportação JSON curta do desktop era um bug ou uma configuração. Agora três superfícies compartilham uma regra, as duas projeções têm justificativas explícitas, e uma sessão grande demais retorna um código específico com um conselho que você pode aplicar diretamente. Se você exporta sessões de forma programada, vale fazer hoje uma verificação: qual superfície e qual formato seu pipeline de backup usa — e se o que ele produz realmente reimporta sem problemas.