hermes - ✅(Solved) Fix Feature: Per-user memory isolation in group chats to prevent identity contamination [2 pull requests, 1 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#11430Fetched 2026-04-18 06:01:08
View on GitHub
Comments
1
Participants
1
Timeline
4
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×2commented ×1referenced ×1

Fix Action

Fixed

PR fix notes

PR #11452: feat: per-user memory isolation in group chats (#11430)

Description (problem / solution / changelog)

Summary

In group chats, the agent's persistent memory (MEMORY.md, USER.md) was globally shared across all users. This caused identity contamination — User A's personal info, preferences, and persona could bleed into conversations with User B.

This PR implements per-user memory isolation by introducing a namespace parameter to MemoryStore. Each platform:user_id combination gets its own memory directory, preventing cross-user contamination.

Related Issues

  • Closes #11430 — Feature: Per-user memory isolation in group chats to prevent identity contamination
  • Related to #10554 — session_search should be scoped to the current chat by default
  • Related to #9514 — Feature: Single-Daemon Multi-Agent with Per-Topic Workspace & Memory Isolation
  • Related to #5195 — Feature: Per-topic/per-group system prompt binding for Telegram
  • Related to #9532 — Bug: background process notifications leak into wrong threaded session (CLOSED)

Changes

tools/memory_tool.py

  • Added namespace: str = "" parameter to get_memory_dir() — routes to ~/.hermes/memories/{namespace}/ when set, falls back to global root for backward compatibility
  • MemoryStore.__init__ now accepts and stores namespace
  • Converted _path_for() and load_from_disk() from static methods to instance methods using self.namespace
  • Added auto-migration logic: on first load with a namespace, copies existing MEMORY.md and USER.md from global root into the namespace directory (preserving original files)

run_agent.py

  • Constructs namespace string _ns = f"{self.platform}:{self._user_id}" (empty string fallback for CLI/unknown contexts)
  • Passes namespace=_ns to MemoryStore() initialization

Design Decisions

  1. Namespace format: platform:user_id (e.g., telegram:5137755622)
  2. Backward compatibility: CLI sessions and contexts without user_id use empty namespace → global ~/.hermes/memories/ root (unchanged behavior)
  3. Auto-migration: On first access, existing root-level memory files are copied into the namespace directory. Originals are preserved, not deleted.
  4. No schema changes: Memory format (Markdown files) stays identical — only the directory changes

Testing

  • Verified namespace directory auto-creation (~/.hermes/memories/telegram_5137755622/)
  • Confirmed CLI fallback to global root (empty namespace)
  • Tested auto-migration copies files correctly without deleting originals

Impact

This is a targeted fix scoped to the memory persistence layer. The session isolation (group_sessions_per_user: True) already handled conversation-level isolation — this PR addresses the missing persistent memory isolation that caused the cross-user identity contamination.

Changed files

  • gateway/run.py (modified, +6/-0)
  • gateway/stream_consumer.py (modified, +14/-8)
  • run_agent.py (modified, +8/-0)
  • tests/tools/test_memory_tool.py (modified, +49/-3)
  • tools/memory_tool.py (modified, +35/-8)

PR #11448: feat(memory): auto-migrate shared root memories to per-user namespace dirs

Description (problem / solution / changelog)

Summary

When per-user memory isolation is enabled, users with existing shared memories (MEMORY.md / USER.md in ~/.hermes/memories/ root) would lose their memories because the new namespaced directory starts empty.

This PR adds automatic migration: on first load for a new namespaced user, shared root memory files are automatically copied into their per-user directory.

Changes

tools/memory_tool.py

  • Add _migrate_from_shared() — helper that copies MEMORY.md and USER.md from the shared root to a namespaced user directory, only if the target files do not already exist (no overwrite)
  • Fix load_from_disk() in MemoryStore.__init__ area — the original definition used get_memory_dir() without the namespace parameter, which would break per-user isolation. Fixed to use get_memory_dir(self.namespace) and call migration.
  • Remove duplicate load_from_disk() — the second definition shadowed the first, making the fix in the first ineffective
  • Fix regex escaping in get_memory_dir() (@.+@\\.+ for proper literal dot matching)

tests/tools/test_memory_tool.py

  • TestMemoryMigrationFromSharedRoot — new test class with 3 tests:
    • test_migration_copies_shared_files_to_new_namespace — verifies happy path
    • test_migration_does_not_overwrite_existing_user_files — verifies no data loss
    • test_migration_does_nothing_for_empty_namespace — verifies CLI/global sessions unaffected

Testing

All 37 tests in test_memory_tool.py pass, including:

  • 3 new migration tests
  • Existing namespace isolation test
  • All existing MemoryStore and memory_tool dispatcher tests

Changed files

  • gateway/run.py (modified, +6/-0)
  • gateway/stream_consumer.py (modified, +14/-8)
  • run_agent.py (modified, +8/-0)
  • tests/tools/test_memory_tool.py (modified, +119/-3)
  • tools/memory_tool.py (modified, +52/-9)
RAW_BUFFERClick to expand / collapse

Problem Description

In group chats with multiple participants, the agent's persistent memory (stored via the memory tool) is globally shared across all users and sessions. This causes the agent to:

  1. Confuse user identities — e.g., attributing user A's personal info (job, habits, preferences) to user B
  2. Cross-contaminate preferences — learning user A's personality style and applying it to user B
  3. Break user trust — users see the agent "remember" things about them that were never discussed

Current Behavior

  • group_sessions_per_user: true isolates conversation sessions per user ✅
  • But memory tool stores to a single global store (USER PROFILE + MEMORY) with no user scoping ❌
  • session_search also returns results from all users across all sessions ❌
  • The system prompt injects the same USER PROFILE for all users in the same session

Expected Behavior

When multiple users interact with the agent in group chats:

  1. Memory should be scoped per user — each user (identified by platform:user_id) gets their own MEMORY and USER PROFILE
  2. Session search should be user-scoped — results only from the current user's sessions
  3. System prompt should reflect the current user's profile — not a different user's preferences

Proposed Solution

Option A: Namespace memory by user identity

  • Prefix memory keys with a user identifier (e.g., telegram:123456)
  • The agent already receives source.user_id and source.platform in the gateway
  • Inject the current user's identity into the system prompt so the agent knows whose context it's in

Option B: Per-user memory stores

  • Create separate memory stores per platform:user_id combination
  • More isolation but requires more storage changes

Scope Considerations

  • DM/private chats: No change needed — already single-user
  • Group chats (no thread): Each user has their own session, but memory is shared — needs fixing
  • Shared threads: Multiple users in one session — memory contamination is worst here

Environment

  • Hermes Agent latest main
  • Platforms: Telegram (primary), also affects Discord, Slack, WhatsApp

extent analysis

TL;DR

Implement user-scoped memory by prefixing memory keys with a user identifier or creating separate memory stores per user to prevent global sharing of agent's persistent memory.

Guidance

  • To address the issue, consider implementing one of the proposed solutions: namespace memory by user identity (Option A) or create per-user memory stores (Option B).
  • Verify the fix by testing group chats with multiple participants and ensuring that the agent's memory is correctly scoped per user, without cross-contamination of preferences or confusion of user identities.
  • When implementing the solution, consider the scope of the changes, including DM/private chats, group chats, and shared threads, to ensure that the fix is applied correctly in each context.
  • To mitigate the issue, focus on modifying the memory tool to store data with user scoping, and update the session_search to return results only from the current user's sessions.

Example

# Example of prefixing memory keys with a user identifier (Option A)
memory_key = f"{source.platform}:{source.user_id}:memory"

Notes

The proposed solutions require changes to the memory tool and potentially the storage infrastructure, and may have implications for storage requirements and data management.

Recommendation

Apply workaround by implementing Option A (namespace memory by user identity) as it seems to be a more straightforward solution that can be implemented with minimal changes to the existing infrastructure.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING