fix(plugin): deterministic mem0 user_id resolution

Hooks previously fell back to $USER when MEM0_USER_ID wasn't set,
producing a different user_id on every machine for the same person.
Result: a single account with memories scattered across many user
buckets, none of which can see each other.

Resolution priority (same in bash and python):
  1. MEM0_USER_ID env var (explicit override)
  2. ~/.mem0/identity.json cache (pinned to MEM0_API_KEY fingerprint)
  3. Derived: "mem0-" + sha256(MEM0_API_KEY)[:12]
  4. Fallback: $USER, else "default"

Same MEM0_API_KEY across machines now yields the same user_id without
the user having to set MEM0_USER_ID by hand on every laptop.

Resolver shipped as two tiny files instead of a shared module:
  _identity.sh -- sourced by bash hooks, exports MEM0_RESOLVED_USER_ID
  _identity.py -- imported by on_pre_compact.py, exposes resolve_user_id()

Hook integration:
  on_user_prompt.sh -- sources resolver, USER_ID interpolated into rubric
  on_session_start.sh -- emits an "Active user_id: <X>" header before the
    bootstrap text, so the agent's MCP search_memories/add_memory calls
    use the same bucket the hooks write to (closes the agent-side half
    of the symptom)
  on_pre_compact.py -- replaces inline env lookup with resolve_user_id()

Existing memories under previous $USER values are not auto-migrated.
The cache file regenerates on key rotation (fingerprint mismatch).
This commit is contained in:
Mgeeeek
2026-05-08 21:02:24 +05:30
parent 8cb4f09ded
commit ed15fc1386
5 changed files with 140 additions and 3 deletions
+5 -2
View File
@@ -18,8 +18,11 @@ import json
import logging
import os
import sys
import urllib.request
import urllib.error
import urllib.request
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from _identity import resolve_user_id
log = logging.getLogger("mem0-capture")
log.setLevel(logging.DEBUG)
@@ -219,7 +222,7 @@ def main():
return
session_id = hook_input.get("session_id", "")
user_id = os.environ.get("MEM0_USER_ID", os.environ.get("USER", "default"))
user_id = resolve_user_id()
lines = tail_lines(transcript_path, MAX_TAIL_LINES)
if not lines: