@alembic/hermes é onde o motor vira agente: memória multi-store (5 substores append-only), um SkillStore com 148 skills, templates determinísticos, leitores de sessão de outros agentes (A4b) e o kernel do AI Employee — 43 contratados em ~/.alembic/employees. O fio condutor é um só: honestidade estrutural — nada fabricado entra em memória, e o que é interpretação nunca se disfarça de fato.
A memória não é um blob: são cinco logs JSONL append-only, um por natureza de registro — episodic, semantic, procedural, decision, transcript (o vocabulário exato está em employee.ts:49–55, employeeMemoryStores, e o composer lê "os cinco readers injetados", multi-store/composer.ts:52). Cada linha é um record tipado, escopado por agent; o CLI expõe tudo em alembic memory <substore> add|list.
| Substore | O que guarda | Forma no CLI |
|---|---|---|
| episodic | Episódios vividos (goal + contexto do turno) | alembic memory episodic add|list |
| semantic | Conceitos e fatos (com confiança e associações) | alembic memory semantic add|list |
| procedural | Como-fazer durável (procedimentos) | alembic memory procedural add|list |
| decision | Decisões tomadas e alternativas | alembic memory decision add|list |
| transcript | Turnos importados de sessões (A4b) | alembic memory transcript add|list |
O que dá segurança ao desenho é o binding: um employee declara QUAIS substores usa (employeeMemoryBindingSchema, employee.ts:67–73). Na hora do turno, composeMemoryText projeta os cinco readers para apenas os bindados — store não bindado vira reader no-op, "so its records never leak into the prompt" (run.ts:191–199). Memória é privilégio declarado, não ambiente compartilhado.
O SkillStore (packages/hermes/src/skills/skill-store.ts:68) é um clone fiel do subsistema de skills do Hermes original: cada skill é um diretório <base>/<nome>/SKILL.md (frontmatter + corpo). A economia de contexto vem da disclosure progressiva: list() lê SÓ o frontmatter de cada SKILL.md — nunca os corpos (skill-store.ts:75–96); view() carrega o corpo inteiro + arquivos de apoio quando você realmente precisa. Toda operação falível devolve Result e nunca lança pela borda pública (skill-store.ts:34–35).
list() pula qualquer entrada que não seja diretório: if (!entry.isDirectory) continue; (skill-store.ts:89). Como o FsPort.readDir reporta um symlink com isDirectory: false, skill symlinkada sob a base não aparece — nem no list, nem no alembic import --skills (que usa o mesmo store). O acervo real de 148 skills em ~/.hermes/skills só conta diretórios reais com SKILL.md. Sintoma clássico: "importei meu diretório de skills e vieram menos" — os que faltam são links.Ao lado do store vivem os templates determinísticos e offline: triageIssue (templates/issue-triage.ts:157) classifica uma issue e extractMeetingNotes (templates/meeting-notes.ts:164) estrutura um transcript — os comandos alembic triage e alembic notes, $0 por default.
O pacote @alembic/sessions lê o histórico de três ecossistemas — readClaudeSession, readCodexSession, readChatGptExport (packages/sessions/src/index.ts:26–28) — e normaliza tudo em SessionRecord. Daí o hermes assume: sessionToTranscriptRecords (memory/transcript-import.ts:51) transforma cada turno num record de transcript com id determinístico import-<sessionId>-<n> e o at vindo da FONTE (nunca Date.now); appendNewTranscriptRecords (transcript-import.ts:122) deduplica por id contra o arquivo existente — reimportar é no-op. No CLI: alembic import <path>, dry-run $0 por default, --write para gravar.
memory/session-distill.ts: ① o prompt exige só o que está EXPLÍCITO no transcript — "NEVER invent, infer beyond the text, or add outside knowledge" (DISTILL_SYSTEM_PROMPT, linha 98); ② todo record carrega proveniência session:<id> + distilled:<modelId> (sessionAssociation/distilledAssociation, linhas 78–81), com tags falsificadas pelo modelo REMOVIDAS; ③ a confiança nasce rebaixada: DISTILLED_MEMORY_CONFIDENCE = 0.4 (linha 66) — abaixo do teto ≤ 0.5 da regra da casa. Resposta malformada/vazia do modelo → resultado VAZIO, nunca um record inventado. E --distill sem --online falha fechado: sem gasto, sem fabricação.Um employee é composição, não código novo (employeeDefinitionSchema, employee.ts:95–108): um soul (identidade + modelPreferences) + skills (ids no SkillStore) + memory binding + declarações de connectors e schedule. renderEmployeePrompt (employee.ts:208) transforma isso em system prompt; composeSkillsText (run.ts:281) injeta os corpos inteiros das skills bindadas como ## Skill Playbooks — o modelo SEGUE as regras da skill, não apenas vê nomes (run.ts:87–92).
O turno em si tem duas metades com temperamentos opostos: buildEmployeeRunInput (run.ts:112–138) é PURO e determinístico — sem clock, sem RNG, sem IO; requestId = employee-<id>; modelo = primary ?? fallback ?? fallbackModelId, senão err — e é ELE que o dry-run $0 imprime ("o prompt que seria enviado"). Já runEmployeeTurn (run.ts:151–189) faz UMA única chamada adapter.run(input) pela cintura estreita da lição 02; memória que falhar na composição degrada para '' — "memory must never break the turn" (run.ts:143–149).
writeback.ts:88–95) — jamais um "learning" sintetizado.episodic no binding completa um turno --online com sucesso. O que o write-back A3b faz?writeback.ts:79–81: sem episodic no binding, recordEmployeeTurn retorna ok(false) e nada é gravado. E mesmo quando grava, uma falha de escrita vira err logado sem quebrar o turno (o turno já foi um sucesso).alembic import --skills --write. O que acontece com os links?skill-store.ts:89 — if (!entry.isDirectory) continue;. O readDir do FsPort marca symlink como isDirectory: false, então só diretórios REAIS com SKILL.md entram. É por isso que plugins symlinkados "somem" do import — e por que os 148 skills contados são todos diretórios de verdade.Tudo offline/$0 por default (o --online é a exceção deliberada):
# memória multi-store: ler e escrever na mão alembic memory semantic list --agent demo --limit 5 alembic memory episodic add --agent demo --record '{"episode":"li a lição 03","context":"curso v3"}' # o employee de ponta a ponta, sem gastar um centavo alembic employee list alembic employee show <id> alembic employee run <id> --goal "resuma seu papel" # dry-run: imprime o prompt e PARA # A4b: importar sessões (preview $0; --write grava idempotente) alembic import ~/.claude/projects/<proj>/<sessao>.jsonl alembic import conversations.json --format chatgpt --write # o distill honesto exige as duas flags — e sem --online falha fechado alembic import <sessao> --distill # err: --distill needs --online # a gotcha do symlink, na fonte grep -n "entry.isDirectory) continue" packages/hermes/src/skills/skill-store.ts
Números da casa: 148 skills no SkillStore (~/.hermes/skills) · 43 AI Employees (~/.alembic/employees) · confiança de record destilado nasce em 0.4 (≤ 0.5 sempre).
run.ts:191–199).list() pula não-diretórios (skill-store.ts:89).import-<sessão>-<n> (transcript-import.ts:31,122).session:+distilled:, confiança 0.4) — interpretação nunca vira fato.unit.proof[] roda comandos reais e falha a run fechada se a prova não sair verde.