| name | agent-memory |
| description | Persistent SQLite memory for Hermes and OpenClaw: Facts, snippets, lessons, entities, relations, provenance (read-only audit-chain reconstruction), finer source trust (tool/external quarantined to evidence), relation-aware plugin recall with opt-in neighbor attributes, conflict detection, Authority Lanes, a procedural lane for self-written behavioral rules (observation-only, human review-gate, rule-conflict detection), Rebound-Protection, and budgeted German-aware query retrieval. |
| version | 3.6.0 |
| author | xPerryx + Lena OpenClaw (agent-memory-1-0-0 base) |
| license | MIT |
| platforms | ["linux","macos"] |
| metadata | {"hermes":{"tags":["memory","sqlite","persistence","facts","snippets","lessons","entities","authority-lanes","plugin","retrieval"],"category":"memory"}} |
AgentMemory Skill
Persistent memory system for Hermes and OpenClaw with structured Authority Lanes, raw recall snippets, Rebound-Protection, and budgeted plugin retrieval.
Inspired by Lena OpenClaw's agent-memory-1-0-0, extended with:
- Authority Lanes (identity / preference / evidence / authorization / procedural)
- Rebound-Protection after idle phases (signalfoundry / Moltbook pattern)
- Class-specific TTL and forget_stale()
- Finer source trust: five sources with per-lane write policy (
tool/external quarantined to evidence; identity/authorization protected)
- Raw recall snippets kept separate from semantic facts
- Auto-injection plugin with per-lane budgets and German-aware, score-ranked query retrieval (token-prefix FTS + synonyms, deterministic, no embeddings)
- Conflict detection on single-valued lanes (
identity, authorization) with explicit resolution; open conflicts auto-reconcile when a referenced fact becomes inactive
- Entity relations: lightweight directed graph between entities (stdlib-only, no embeddings)
- Relation-aware plugin recall: bounded 1-hop relation expansion on query turns (edge-only, budgeted); opt-in neighbor entity attributes via
AGENT_MEMORY_BUDGET_ENTITY_ATTRS
- Provenance: read-only reconstruction of a fact's audit chain via
get_provenance() and CLI provenance (derived from the append-only audit log; no duplicate storage)
When to Use
Load this skill when you want persistent memory across Hermes sessions that:
- Survives restarts
- Separates identity facts from preferences and technical evidence
- Keeps raw conversation snippets searchable without auto-injecting them
- Prevents memory injection attacks (authorization only from observation)
- Auto-loads bounded context at session start and retrieves relevant evidence later
Installation
1. Copy files
HERMES=~/.hermes
mkdir -p $HERMES/agent-memory/src
mkdir -p $HERMES/agent-memory/cli
mkdir -p $HERMES/agent-memory/tests
cp src/memory.py $HERMES/agent-memory/src/
cp cli/fact.py $HERMES/agent-memory/cli/
cp tests/test_memory.py $HERMES/agent-memory/tests/
cp tests/test_plugin.py $HERMES/agent-memory/tests/
2. Install plugin
mkdir -p $HERMES/plugins/agent-memory-plugin
cp plugin/__init__.py $HERMES/plugins/agent-memory-plugin/
cp plugin/plugin.yaml $HERMES/plugins/agent-memory-plugin/
3. Enable plugin in config.yaml
plugins:
enabled:
- agent-memory-plugin
4. (Optional) systemd cleanup timer
systemctl --user enable --now hermes-memory-cleanup.timer
5. Verify
cd ~/.hermes/agent-memory
python3 -m pytest tests -v
Authority Lanes
| Class | TTL | Min Confidence | Allowed Sources | Notes |
|---|
| identity | NEVER | 0.9 | observation, conversation | Floor — never expires |
| preference | 14d | 0.3 | observation, conversation | Tone, style, language |
| evidence | 60d | 0.5 | observation, conversation, inference, tool, external | Quarantine for lower-trust input |
| authorization | 90d | 0.9 | observation ONLY | Never from conversation/tool/external |
| procedural | 30d | 0.5 | observation ONLY | Self-written behavioral rules; own table, human review-gate, never auto-active |
Procedural Lane
Behavioral rules ("how to respond"), kept separate from facts ("what is true")
in a procedural_rules table. Observation-only writes via propose_rule();
every rule starts pending and only approve_rule() (a mandatory human gate, no
auto-approve) makes it active. Deterministic, stdlib-only conflict detection on
approval hard-blocks direct contradictions and soft-blocks interactions /
artifact bloat / budget overflow (override with ack_interactions). The plugin
injects only active, trigger-matching rules in a sanitized, budgeted
## Procedural Rules block. CLI: propose-rule, pending-rules,
active-rules, approve-rule, reject-rule, retire-rule, rule-conflicts.
Rebound-Protection
After >6h idle: max 3 new facts accepted per session (except identity).
Prevents memory flooding after long offline phases.
Sliding TTL
Non-identity facts expire by last access, not only by creation time. recall(),
recall_by_authority(), and get_fact() all refresh last_accessed and extend
expires_at according to the fact's authority lane. This keeps facts alive when
the auto-injection plugin actively uses them.
Python Usage
import sys
sys.path.insert(0, str(Path.home() / '.hermes/agent-memory/src'))
from memory import AgentMemory
mem = AgentMemory()
mem.remember("User's name is Alex", authority_class="identity",
source="observation", confidence=1.0)
mem.remember("Prefers German language", authority_class="preference",
source="observation", confidence=0.9)
facts = mem.recall("name")
identity = mem.recall_by_authority("identity")
mem.learn(action="Deployed without fallback",
context="hermes-setup", outcome="negative",
insight="Always configure a fallback provider")
mem.track_entity("Alex", "person", {"username": "alex_dev", "language": "de"})
mem.relate("Alex", "arbeitet_bei", "acme")
mem.get_relations("Alex", direction="both")
mem.get_conflicts()
mem.remember_snippet("Discussed query-aware retrieval", session_id="demo")
snippets = mem.search_snippets(, session_id=)
(mem.stats())
mem.forget_stale()
CLI Usage
PYTHON=python3
CLI=~/.hermes/agent-memory/cli/fact.py
$PYTHON $CLI add "User name is Alex" --authority identity --source observation --confidence 1.0
$PYTHON $CLI recall "name"
$PYTHON $CLI list --authority identity
$PYTHON $CLI stats
$PYTHON $CLI snippet add "Discussed local-first retrieval" --session demo
$PYTHON $CLI snippet search retrieval --session demo
$PYTHON $CLI audit --limit 10
$PYTHON $CLI snapshot --label before-change
$PYTHON $CLI provenance <fact_id>
$PYTHON $CLI consolidate --dry-run
$PYTHON $CLI forget-stale
$PYTHON $CLI learn "Action" "Context" positive "Insight"
$PYTHON $CLI relate Alex arbeitet_bei acme
$PYTHON $CLI relations Alex --direction out
$PYTHON $CLI conflicts
resolve-conflict <keep_id> <drop_id>
Auto-Injection via Plugin
Once installed, the plugin builds memory context automatically via the pre_llm_call hook.
First turn:
- identity: permanent floor, budget-limited
- preference: last 5
- evidence: last 10
- negative lessons: last 3
Later turns:
- no injection unless the hook provides a current user message
- identity remains available as a small floor
- relevant evidence is retrieved from the user message
- when entities are mentioned, direct relations are injected under
## Related (1-hop, edge-only; opt-out via AGENT_MEMORY_RELATIONS; optional neighbor attributes via AGENT_MEMORY_BUDGET_ENTITY_ATTRS, default disabled)
- all lanes are clipped by per-lane budgets
authorization facts are never prompt-injected. They are allowed only from
observation source and should be used by explicit code paths, not automatic
context injection.
No manual loading required.
Pitfalls
- On some systems
python is not in PATH — use python3 or full venv path.
- SQLite
:memory: loses data when connection closes. Tests use _shared_conn pattern — do not change connection logic without understanding this.
_check_rebound() must run after _init_db() — memory_meta must exist first.
startup_skills alone is NOT enough for auto-injection. The plugin with pre_llm_call hook is required.
authorization facts from conversation source are silently rejected by design.
- Raw snippets are recall memory, not facts. Store them with
remember_snippet() and search them with search_snippets().
- The plugin uses character budgets instead of a tokenizer to avoid extra runtime dependencies.
Architecture Decisions
- Floor (identity) never decays — idle periods must not lower the entry threshold.
- Rebound-Cap: After >6h idle, max 3 new facts — prevents memory flooding. Identity is exempt.
- Sliding TTL: Read access refreshes non-identity expiry, so active facts survive cleanup.
- Recall snippets are separate: raw conversation memory does not pollute semantic facts and is not auto-injected.
- Prompt budgets: plugin context is clipped per lane to keep first-turn and later-turn prompts bounded.
- Timer as compactor only — writing is event-driven (on
remember()), not time-based.
- authorization only from observation — prevents privilege escalation via conversation.
- authorization never auto-injected — sensitive permission memory is not placed into prompts by default.
- forget_stale() class-aware — identity: never, preference: 14d, evidence: 60d, authorization: 90d; procedural rules expire after 30d (status -> expired).
- procedural only from observation, never auto-active — self-written behavior rules require a human review-gate; only approved rules are injected.
References
- See
references/architecture.md for design rationale
- See
references/moltbook-discussion.md for signalfoundry pattern origin