230 lines
9.3 KiB
Markdown
230 lines
9.3 KiB
Markdown
---
|
|
name: memory-audit-maintenance
|
|
kind: responsibility
|
|
id: 067NC4KG01RG50R40M30E20918
|
|
---
|
|
|
|
### Goal
|
|
|
|
Autonomously audit and reorganize an agent's native memory (MEMORY.md, USER.md, SOUL.md), categorize entries, redistribute them to correct locations, verify live configurations, and free character budget all without user intervention unless a truly unresolvable ambiguity exists.
|
|
|
|
### Requires
|
|
|
|
(none this is a self-driven, autonomous node)
|
|
|
|
### Maintains
|
|
|
|
Memory health state current utilization, last audit timestamp, and any unresolved ambiguities. Postcondition: no entry is duplicated, no session note or stale snapshot survives, and no config is unverified.
|
|
|
|
#### utilization
|
|
Current character utilization of MEMORY.md and USER.md. Material: the percentages.
|
|
|
|
#### last_audit
|
|
Timestamp of the most recent successful audit. Material: the timestamp.
|
|
|
|
#### unresolved
|
|
Any entries that could not be autonomously categorized. Material: the entry text and the reason for ambiguity.
|
|
|
|
### Continuity
|
|
|
|
- self-driven: re-audit every 14 days (2 weeks)
|
|
- input-driven: triggered by explicit user request ("memory audit", "memory maintenance", "clean up memory", "memory is full")
|
|
- threshold: trigger immediately if either store exceeds 85% utilization
|
|
|
|
### Parameters
|
|
|
|
- `memory_threshold`: percentage that triggers an immediate audit (default: 85)
|
|
- `max_memory_chars`: maximum allowed characters for MEMORY.md (default: 2200)
|
|
- `max_user_chars`: maximum allowed characters for USER.md (default: 1375)
|
|
- `compress_entries`: whether to rewrite entries for brevity (default: true)
|
|
- `dry_run`: whether to only report changes without applying them (default: false)
|
|
|
|
### Strategies
|
|
|
|
- **Autonomous first:** Never ask the user about entries that clearly belong in a deletion or move category. Only escalate on true ambiguity (e.g., an entry could be either a Preference or a Technical Config, and the choice affects which file it goes in).
|
|
- **Incremental over nuclear:** Always use patch-level edits (find-and-replace) rather than wholesale file rewrites. If the patch tool fails, fall back to rewrite only as a last resort.
|
|
- **Verify before trusting:** Every technical config in MEMORY.md must be verified against the live system. If a service is unreachable, mark it as `UNVERIFIED` rather than deleting it.
|
|
- **Stale state has a shelf life:** Any entry marked as "Temporary State" (e.g., "verbal offer", "deal pending", "negotiation phase") is deleted if older than 14 days.
|
|
- **Work around tool failures:** If the `memory` tool fails after 2 retries, add a summary marker and report the stale state in the unresolved list.
|
|
|
|
### Invariants
|
|
|
|
- No entry is deleted without being categorized first
|
|
- No duplicate entries survive across MEMORY.md and USER.md
|
|
- No session note or temporary state survives past 14 days
|
|
- No config is trusted without verification (or marked UNVERIFIED)
|
|
|
|
### Execution
|
|
|
|
```prose
|
|
# ============================================================
|
|
# PHASE 1: Pre-flight
|
|
# ============================================================
|
|
|
|
# Verify the memory tool works
|
|
try:
|
|
memory_status = call check_memory_tool
|
|
catch:
|
|
memory_status = "BROKEN"
|
|
|
|
# Read current state
|
|
memory_content = read_file("~/.hermes/memories/MEMORY.md")
|
|
user_content = read_file("~/.hermes/memories/USER.md")
|
|
|
|
# Calculate current utilization
|
|
memory_util = len(memory_content) / max_memory_chars * 100
|
|
user_util = len(user_content) / max_user_chars * 100
|
|
|
|
# Check if audit is needed
|
|
if last_audit > 14 days and memory_util < 75 and user_util < 75:
|
|
return {status: "SKIP", reason: "No audit needed"}
|
|
|
|
# ============================================================
|
|
# PHASE 2: Categorize every entry
|
|
# ============================================================
|
|
|
|
categorized = []
|
|
for entry in split_entries(memory_content):
|
|
cat = call categorize_entry(entry)
|
|
categorized.append({entry, category: cat, source: "MEMORY.md"})
|
|
|
|
for entry in split_entries(user_content):
|
|
cat = call categorize_entry(entry)
|
|
categorized.append({entry, category: cat, source: "USER.md"})
|
|
|
|
# ============================================================
|
|
# PHASE 3: Identify actions
|
|
# ============================================================
|
|
|
|
actions = []
|
|
for item in categorized:
|
|
if item.category in ["Session Note", "Temporary State"]:
|
|
# Check age of temporary state
|
|
if item.category == "Temporary State" and item.age < 14 days:
|
|
continue # Too young to delete
|
|
actions.append({action: "DELETE", entry: item.entry, reason: item.category})
|
|
elif item.category == "Historical Snapshot":
|
|
actions.append({action: "DELETE", entry: item.entry, reason: "Stale"})
|
|
elif item.category == "Technical Config" and item.source == "USER.md":
|
|
actions.append({action: "MOVE", from: "USER.md", to: "MEMORY.md", entry: item.entry})
|
|
elif item.category == "Identity" or item.category == "Preference" and item.source == "MEMORY.md":
|
|
actions.append({action: "MOVE", from: "MEMORY.md", to: "USER.md", entry: item.entry})
|
|
elif item.category == "Operating Rule":
|
|
actions.append({action: "MOVE", from: "MEMORY.md", to: "Skill file", entry: item.entry})
|
|
|
|
# Check for duplicates
|
|
for entry_a, entry_b in find_duplicates(categorized):
|
|
actions.append({action: "DELETE", entry: entry_b.entry, reason: "Duplicate of " + entry_a.entry})
|
|
|
|
# ============================================================
|
|
# PHASE 4: Verify live configs
|
|
# ============================================================
|
|
|
|
for item in categorized:
|
|
if item.category == "Technical Config" and item.entry contains URL:
|
|
result = call verify_endpoint(item.entry)
|
|
if result == "UNREACHABLE":
|
|
# Mark as UNVERIFIED, do not delete
|
|
actions.append({action: "MARK_UNVERIFIED", entry: item.entry})
|
|
elif result == "VERIFIED":
|
|
pass # Good, nothing to do
|
|
|
|
for item in categorized:
|
|
if item.category == "Technical Config" and item.entry contains "model:":
|
|
result = call verify_model(item.entry)
|
|
if result == "MISMATCH":
|
|
actions.append({action: "CORRECT", entry: item.entry, corrected: result.new_value})
|
|
|
|
# ============================================================
|
|
# PHASE 5: Apply changes
|
|
# ============================================================
|
|
|
|
if dry_run:
|
|
return {status: "DRY_RUN", actions: actions, summary: generate_summary(actions)}
|
|
|
|
# Apply deletions
|
|
for action in actions:
|
|
if action.action == "DELETE":
|
|
try:
|
|
memory action=remove target=action.source old_text=action.entry
|
|
catch after 2 retries:
|
|
patch path=action.source old_string=action.entry new_string=""
|
|
# Add summary marker since memory tool is broken
|
|
memory action=add target=action.source content="Entry deleted via patch: " + action.entry
|
|
|
|
# Apply moves
|
|
for action in actions:
|
|
if action.action == "MOVE":
|
|
# Delete from source
|
|
patch path=action.from old_string=action.entry new_string=""
|
|
# Add to destination
|
|
append_file path=action.to content=action.entry + "\n"
|
|
|
|
# Apply corrections
|
|
for action in actions:
|
|
if action.action == "CORRECT":
|
|
patch path="~/.hermes/memories/MEMORY.md" old_string=action.entry new_string=action.corrected
|
|
|
|
# Apply UNVERIFIED marks
|
|
for action in actions:
|
|
if action.action == "MARK_UNVERIFIED":
|
|
patch path="~/.hermes/memories/MEMORY.md" old_string=action.entry new_string=action.entry + " [UNVERIFIED]"
|
|
|
|
# ============================================================
|
|
# PHASE 6: Post-audit verification
|
|
# ============================================================
|
|
|
|
new_memory_content = read_file("~/.hermes/memories/MEMORY.md")
|
|
new_user_content = read_file("~/.hermes/memories/USER.md")
|
|
|
|
new_memory_util = len(new_memory_content) / max_memory_chars * 100
|
|
new_user_util = len(new_user_content) / max_user_chars * 100
|
|
|
|
# Add summary markers to in-memory stores
|
|
memory action=add target=memory content="MEMORY.md rewritten. Utilization: " + new_memory_util + "%."
|
|
memory action=add target=user content="USER.md rewritten. Utilization: " + new_user_util + "%."
|
|
|
|
# ============================================================
|
|
# PHASE 7: Report
|
|
# ============================================================
|
|
|
|
return {
|
|
status: "COMPLETE",
|
|
memory_utilization: new_memory_util,
|
|
user_utilization: new_user_util,
|
|
actions_applied: len(actions),
|
|
deletions: count(actions, "DELETE"),
|
|
moves: count(actions, "MOVE"),
|
|
corrections: count(actions, "CORRECT"),
|
|
unverified: count(actions, "MARK_UNVERIFIED"),
|
|
unresolved: unresolved_entries,
|
|
last_audit: now(),
|
|
summary: generate_summary(actions)
|
|
}
|
|
```
|
|
|
|
### Shape
|
|
|
|
- `self`: categorize, verify configs, apply incremental patches, generate reports
|
|
- `delegates`:
|
|
- `categorize_entry`: determine the category of a memory entry
|
|
- `check_memory_tool`: verify the memory tool is functional
|
|
- `verify_endpoint`: check if a URL is reachable
|
|
- `verify_model`: check if a model config matches live config.yaml
|
|
- `find_duplicates`: detect duplicate entries across files
|
|
- `generate_summary`: produce a human-readable summary of actions
|
|
|
|
### Tools
|
|
|
|
- `file:read`: read MEMORY.md and USER.md
|
|
- `file:patch`: incremental edits via find-and-replace
|
|
- `file:append`: append entries when moving between files
|
|
- `memory:action`: add/remove/replace entries in the in-memory store
|
|
- `network:curl`: verify endpoint reachability
|
|
- `cli:grep`: verify model configs against config.yaml
|
|
|
|
### Environment
|
|
|
|
- `MEMORY_PATH`: ~/.hermes/memories/MEMORY.md
|
|
- `USER_PATH`: ~/.hermes/memories/USER.md
|
|
- `CONFIG_PATH`: ~/.hermes/config.yaml |