Skip to content

Align Hermes passive DKG memory recall#389

Open
Jurij89 wants to merge 1 commit intomainfrom
codex/hermes-six-layer-passive-memory
Open

Align Hermes passive DKG memory recall#389
Jurij89 wants to merge 1 commit intomainfrom
codex/hermes-six-layer-passive-memory

Conversation

@Jurij89
Copy link
Copy Markdown
Contributor

@Jurij89 Jurij89 commented May 4, 2026

Source issue

Closes #385

Summary

  • Rebased onto latest main (200d987b) after additional PRs merged, preserving main's newer Hermes setup parity, dkg_share, context-graph privacy, and build behavior.
  • Replaced Hermes passive prefetch() assertion-only recall with the same six-layer DKG fan-out helper used by memory_search.
  • Added provenance-rich passive recall blocks with context graph id, DKG view, OpenClaw-style layer label, source, score, and path.
  • Added scoped dkg_memory.context_graph_id support while keeping unscoped simple memory writes defaulted to agent-context / memory.
  • Added provider-side and daemon-side stripping for adapter-generated recalled-memory blocks before Hermes chat persistence/import.
  • Addressed review findings for legacy configured-memory fallback, malformed recall-tag preservation, and short-literal recall.

OpenClaw parity notes

  • Hermes passive recall always queries agent-context WM/SWM/VM.
  • Hermes passive recall also queries project WM/SWM/VM only when the calling surface supplies a selected project via context_graph_id / target_context_graph; static context_graph config does not count as current selection.
  • Layer labels match OpenClaw: agent-context-wm, agent-context-swm, agent-context-vm, project-wm, project-swm, project-vm.
  • Trust ranking matches OpenClaw weighting: WM x1.0, SWM x1.15, VM x1.3.
  • Recall results dedupe by context graph plus memory URI/text hash, keeping the higher-trust layer for the same memory.
  • No adapter-to-adapter imports were added; OpenClaw remains the parity reference.

Files changed

  • packages/adapter-hermes/hermes-plugin/__init__.py
  • packages/adapter-hermes/test/hermes-adapter.test.ts
  • packages/cli/src/daemon/routes/hermes.ts
  • packages/cli/test/daemon-hermes.test.ts
  • packages/adapter-hermes/README.md
  • docs/setup/SETUP_HERMES.md
  • packages/cli/skills/dkg-node/SKILL.md

Behavioral details

  • Passive prefetch uses a shared Hermes-local helper with memory_search for query planning, fan-out, ranking, dedupe, and provenance.
  • agent-context recall always runs across WM/SWM/VM. A selected project graph is included only when supplied through context_graph_id / target_context_graph on the call.
  • Passive injected context is emitted as <recalled-memory data-source="dkg-auto-recall"> with escaped snippet text and untrusted-context framing.
  • dkg_memory.context_graph_id stores scoped notes under separate cache buckets and queues writes with the target graph id. Unscoped writes continue to target agent-context / memory.
  • Existing Hermes profiles that previously stored provider memory in a custom configured context_graph are read as a legacy fallback when the new agent-context assertion is empty or unavailable.
  • memory_search no longer filters out short literals with a hard STRLEN >= 20 cutoff.
  • Malformed recalled-memory openings without a closing tag are preserved instead of truncating persisted assistant text.

Test evidence

Latest rebase sanity, after updating onto main at 200d987b:

  • python -m py_compile packages/adapter-hermes/hermes-plugin/__init__.py packages/adapter-hermes/hermes-plugin/client.py passed.
  • git diff --check upstream/main...HEAD passed.
  • pnpm --filter @origintrail-official/dkg-adapter-hermes test -- hermes-adapter.test.ts passed: 86 tests.
  • pnpm --filter @origintrail-official/dkg-core build passed; this refreshed workspace exports after pulling main.
  • pnpm --filter @origintrail-official/dkg build passed.
  • pnpm --filter @origintrail-official/dkg test -- test/daemon-hermes.test.ts --reporter=verbose passed: 66 tests. The command still prints the known post-run Windows message, The system cannot find the path specified., after exiting successfully.

Earlier parity/review-round evidence retained from the prior rebase:

  • pnpm --filter @origintrail-official/dkg-adapter-openclaw test -- before-prompt-build-hook.test.ts memory-integration.test.ts memory-search-tool.test.ts ChatTurnWriter.test.ts ChatTurnWriter.sanitize.test.ts plugin.test.ts dkg-client.test.ts passed: 7 files, 451 tests.
  • pnpm --filter @origintrail-official/dkg-agent... build passed.
  • pnpm --filter @origintrail-official/dkg-epcis build passed.

Manual verification status

  • No live Hermes gateway or Node UI E2E was run in this workspace.
  • Targeted Python provider, daemon route, OpenClaw parity, dependency-build, and CLI build coverage passed locally.
  • Hermes provider and QA/security teammate read-only sanity passes found no blockers after the latest rebase.

Security / persistence notes

  • Recalled-memory blocks are treated as read-only prompt context and are stripped before provider sync_turn() persistence.
  • /api/hermes-channel/persist-turn also strips adapter-generated recalled-memory blocks before chat storage, transition recording, and extraction import.
  • Strip tests cover mixed-case sentinel tags, single-quoted data-source attributes, and malformed unmatched openings so recalled context does not boomerang or cause data loss.
  • Scoped dkg_memory.context_graph_id avoids cache and queue bleed between default personal memory and project notes.
  • Legacy custom-graph provider memory fallback is read-only for recall; new unscoped dkg_memory writes still use agent-context unless context_graph_id is explicit.

Known risks or follow-ups

  • No broad refactor into a neutral cross-adapter recall module was attempted in this focused PR.

Comment thread packages/adapter-hermes/hermes-plugin/__init__.py
Comment thread packages/adapter-hermes/hermes-plugin/__init__.py
Comment thread packages/adapter-hermes/hermes-plugin/__init__.py Outdated
@Jurij89 Jurij89 force-pushed the codex/hermes-six-layer-passive-memory branch from ba083f7 to 5f20c49 Compare May 5, 2026 17:52
Comment thread packages/adapter-hermes/hermes-plugin/__init__.py
Comment thread packages/adapter-hermes/hermes-plugin/__init__.py Outdated
@Jurij89 Jurij89 force-pushed the codex/hermes-six-layer-passive-memory branch from 5f20c49 to 35975da Compare May 5, 2026 18:00
Comment thread packages/adapter-hermes/hermes-plugin/__init__.py Outdated
@Jurij89 Jurij89 force-pushed the codex/hermes-six-layer-passive-memory branch from 35975da to 504ddc0 Compare May 5, 2026 18:06
facts = self._query_memory_facts(assertion_name, _DEFAULT_MEMORY_CONTEXT_GRAPH)
except Exception as e:
logger.debug(f"[dkg] Recall from assertion failed: {e}")
if facts:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Bug: This early return makes the legacy fallback stop working as soon as agent-context has any note. For upgraded profiles that already have provider memory in a custom context_graph, the first new default write will hide all older notes even though this PR documents that they should remain visible. Merge/dedupe the legacy facts with the new agent-context facts (or migrate them) instead of returning here, and add a regression test for the mixed-data case.

score = _keyword_overlap(text, keywords)
layer = _memory_search_layer(cg, view)
source = "sessions" if cg == "agent-context" else "memory"
source = "sessions" if cg == _DEFAULT_MEMORY_CONTEXT_GRAPH else "memory"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Issue: agent-context hits are all tagged as source: "sessions", but dkg_memory also stores durable provider notes in agent-context via urn:hermes:content. That makes the new provenance fields inaccurate and prevents callers from distinguishing simple memory notes from chat-session recall. Derive source from the predicate/assertion type here so Hermes memory entries still report memory.

@Jurij89 Jurij89 force-pushed the codex/hermes-six-layer-passive-memory branch from 504ddc0 to a5c18cb Compare May 6, 2026 11:29
const sentinelOpen =
/<recalled-memory\b(?=[^>]*\bdata-source\s*=\s*(?:"dkg-auto-recall"|'dkg-auto-recall'))[^>]*>/i;
return text.replace(
new RegExp(sentinelOpen.source + /[\s\S]*?<\/recalled-memory>/.source, 'gi'),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Bug: This only removes well-formed recalled-memory pairs. If the model output is truncated after the sentinel open tag, the recalled snippets survive persistence and get re-indexed on later turns, which is exactly the contamination this change is trying to prevent. Strip sentinelOpen...$ as well here (and keep the Python helper aligned).

target = args.get("target", "memory")
content = args.get("content", "")
old_text = args.get("old_text", "")
context_graph_id = _first_text(args, "context_graph_id") or _DEFAULT_MEMORY_CONTEXT_GRAPH
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Bug: context_graph_id now controls where dkg_memory writes, but a malformed non-string value falls through _first_text(...) and silently writes to agent-context. That turns a bad tool call into a data-routing bug. Validate context_graph_id when the key is present and return tool_error on non-string input, like the other DKG handlers do.

facts = self._query_memory_facts(assertion_name, _DEFAULT_MEMORY_CONTEXT_GRAPH)
except Exception as e:
logger.debug(f"[dkg] Recall from assertion failed: {e}")
if facts:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Bug: Returning here means legacy notes disappear as soon as agent-context contains even one new fact. Upgraded profiles with historical memory in a custom context_graph will lose all of that older context after the first unscoped write. Either merge legacy + default facts during recall or migrate the legacy assertion forward instead of treating it as an all-or-nothing fallback.

@Jurij89 Jurij89 force-pushed the codex/hermes-six-layer-passive-memory branch from a5c18cb to af1aeca Compare May 6, 2026 11:35
target = args.get("target", "memory")
content = args.get("content", "")
old_text = args.get("old_text", "")
context_graph_id = _first_text(args, "context_graph_id") or _DEFAULT_MEMORY_CONTEXT_GRAPH
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Bug: context_graph_id is read through _first_text, so any non-string value is silently treated as "missing" and the write falls back to agent-context. A malformed tool call like { "context_graph_id": ["project-cg"] } would store the note in personal memory with no error. Please validate that overrides are non-empty strings before defaulting, and apply the same guard in memory_search, which uses the same parsing pattern.


return self._cache.get("memory", []) + self._cache.get("user", [])
legacy_context_graph = self._legacy_memory_context_graph()
if legacy_context_graph:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Bug: this legacy read now runs unconditionally for every non-default self._context_graph, but the docs and surrounding comments describe it as a fallback only when agent-context is empty. As written, any future dkg_memory(..., context_graph_id=self._context_graph) note will be merged back into _recall_facts() and leak into the default provider memory prompt. Gate the legacy query behind an empty-default-memory check (or a one-time migration flag) instead of always merging it.

@Jurij89 Jurij89 force-pushed the codex/hermes-six-layer-passive-memory branch from af1aeca to e515e53 Compare May 6, 2026 11:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Align Hermes passive memory recall with OpenClaw six-layer DKG memory behavior

1 participant