Skip to content

feat: profile-isolated memory via HERMES_HOME - #37

Open
brian-doherty wants to merge 1 commit into
ClaudioDrews:mainfrom
brian-doherty:feat/profile-isolated-memory
Open

feat: profile-isolated memory via HERMES_HOME#37
brian-doherty wants to merge 1 commit into
ClaudioDrews:mainfrom
brian-doherty:feat/profile-isolated-memory

Conversation

@brian-doherty

Copy link
Copy Markdown
Contributor

Summary

Hermes Agent supports multiple profiles (hermes profile create <name>). When a non-default profile is active, Hermes sets HERMES_HOME to <root>/profiles/<name> (e.g. ~/.hermes/profiles/fpvdeals) and propagates it into subprocess/cron environments.

Previously Memory OS hardcoded ~/.hermes in 15+ places, so every profile stomped on the default profile's memory — shared (colliding) fabric, state.db, memory_store.db, logs, wiki state, DLQ, and SOUL.md. This PR makes Memory OS profile-aware so separate profiles get separate memories.

What changed

  • New scripts/hermes_env.py — single resolution module: hermes_home() honors HERMES_HOME (fallback ~/.hermes), plus fabric_dir(), state_db(), memory_store_db(), logs_dir(), wiki/DLQ/SOUL path helpers.
  • icarus/state.pyFABRIC_DIR defaults to <profile-home>/fabric when a profile is active; agent name auto-detected from modern profiles/<name> and legacy .hermes-<name> layouts; icarus state files (training job, model registry, telemetry, creative state) resolve under the active home (previously fell back to ~ when HERMES_HOME was unset).
  • icarus/hooks.py + icarus/fabric-retrieve.pystate.db / memory_store.db / fabric resolved profile-aware.
  • All maintenance scripts (decay_scanner, semantic_dedup, dlq_manager, wiki_continuous_ingest, reflection_trigger, context_enhancer, verify_soul_config, backfill_decay_metadata) and setup/setup_db.py now use hermes_env paths.
  • setup.sh — new --profile <name> flag installs into ~/.hermes/profiles/<name>.
  • Docsicarus/README.md profile section rewritten for the modern layout.

Behavior

Before After
Default profile ~/.hermes/state.db, ~/fabric unchanged
Profile fpvdeals ~/.hermes/state.db, ~/fabric (collision!) ~/.hermes/profiles/fpvdeals/state.db, .../fpvdeals/fabric
Explicit FABRIC_DIR wins still wins (deliberate cross-profile fabric sharing)

Fully backwards compatible: with HERMES_HOME unset, every path resolves to ~/.hermes exactly as before.

Test plan

  • python -m compileall -q icarus scripts setup _test_collapse.py _test_sanitize.py
  • python _test_collapse.py — all pass ✅
  • python _test_sanitize.py — all 24 pass ✅
  • Resolution checks for default / modern profile / legacy profile layouts (fabric, state.db, agent name) ✅
  • Maintenance scripts run clean against a live install ✅

Hermes Agent supports multiple profiles — when a non-default profile is
active, Hermes sets HERMES_HOME to <root>/profiles/<name>. Previously
Memory OS hardcoded ~/.hermes everywhere, so every profile stomped on
the default profile's memory (fabric, state.db, memory_store.db, logs,
wiki state, DLQ, SOUL.md).

This change makes all Hermes-owned paths resolve through HERMES_HOME:

- New scripts/hermes_env.py: single resolution module (hermes_home(),
  fabric_dir(), state_db(), memory_store_db(), logs_dir(), wiki/DLQ
  paths). Honors HERMES_HOME, falls back to ~/.hermes.
- icarus/state.py: FABRIC_DIR defaults to <profile-home>/fabric when a
  profile is active; agent name auto-detected from modern
  profiles/<name> and legacy .hermes-<name> layouts; icarus state
  files (training job, model registry, telemetry, creative state) now
  resolve under the active home. Profile detection (hermes_home /
  profile_name / is_profile) is imported from scripts/hermes_env
  instead of being duplicated, so there's one source of truth.
- icarus/hooks.py + fabric-retrieve.py: state.db / memory_store.db /
  fabric resolved profile-aware.
- All maintenance scripts (decay_scanner, semantic_dedup, dlq_manager,
  wiki_continuous_ingest, reflection_trigger, context_enhancer,
  verify_soul_config, backfill_decay_metadata) and setup/setup_db.py
  now use hermes_env paths.
- setup.sh: new --profile <name> flag installs into
  ~/.hermes/profiles/<name>. HERMES_HOME is now exported so subprocesses
  (setup_db.py) actually see it, the profile name is validated against a
  safe charset, the generated wiki-watcher cron entry carries HERMES_HOME
  inline and uses a per-profile marker (system crontab doesn't propagate
  env the way Hermes cron does), and FABRIC_DIR is no longer force-written
  into a profile's .env — profiles now default to their own
  <HERMES_HOME>/fabric instead of colliding on the shared
  ${VAULT_PATH}/fabric.
- Docs: icarus/README.md profile section rewritten for the modern
  layout; explains per-profile isolation and how to deliberately share
  a fabric via explicit FABRIC_DIR.
- Tests: _test_hermes_env.py covers hermes_env/state.py path resolution
  (default, modern profiles/<name>, legacy .hermes-<name>, FABRIC_DIR
  override); _test_setup_profile.py covers setup.sh's --profile parsing,
  validation, HERMES_HOME export, and per-profile cron marker in
  isolation.

Backwards compatible: with HERMES_HOME unset everything resolves to
~/.hermes exactly as before; an explicit FABRIC_DIR env var still wins
(cross-profile fabric sharing).
@brian-doherty
brian-doherty force-pushed the feat/profile-isolated-memory branch from 168aeb8 to fc3634c Compare August 3, 2026 18:13
@brian-doherty

Copy link
Copy Markdown
Contributor Author

@ClaudioDrews For your consideration.

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.

1 participant