ed5ee7b9ff
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).
CI nudge in pyproject.toml because path filters in ci.yml exclude
plugin-only PRs but build_mem0/build_embedchain are required.
Manual verification: same key on different $USER values resolves to
identical user_id; MEM0_USER_ID override bypasses cache and key
derivation; cache invalidates on key change; bash and python
implementations produce identical output for all four priority levels.
70 lines
2.5 KiB
Bash
Executable File
70 lines
2.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Hook: SessionStart (matcher: startup|resume|compact)
|
|
#
|
|
# Bootstraps mem0 context at the start of every session.
|
|
# Output becomes part of Claude's context so it calls mem0 MCP tools.
|
|
#
|
|
# Input: JSON on stdin with session_id, source, transcript_path, model, cwd
|
|
# Output: Text injected into Claude's context (exit 0)
|
|
|
|
# Intentionally omit -e so the script always outputs a bootstrap prompt
|
|
# even if jq is missing or stdin is malformed.
|
|
set -uo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
# shellcheck source=_identity.sh
|
|
. "$SCRIPT_DIR/_identity.sh"
|
|
|
|
INPUT=$(cat)
|
|
SOURCE=$(echo "$INPUT" | jq -r '.source // "startup"' 2>/dev/null || echo "startup")
|
|
|
|
# Identity line is emitted before every bootstrap variant so the agent
|
|
# uses the same user_id the hooks resolved. Without this, the agent's
|
|
# search_memories/add_memory MCP calls may bind to a different bucket
|
|
# than what the hooks write to.
|
|
echo "## Mem0 Identity"
|
|
echo ""
|
|
echo "Active user_id: \`$MEM0_RESOLVED_USER_ID\`"
|
|
echo ""
|
|
echo "Always include \`{\"user_id\": \"$MEM0_RESOLVED_USER_ID\"}\` (wrapped in an \`AND\` clause) in every \`search_memories\` filter and as \`user_id\` on every \`add_memory\` call. This keeps memories under one bucket regardless of which machine you're on."
|
|
echo ""
|
|
|
|
if [ "$SOURCE" = "startup" ]; then
|
|
cat <<'EOF'
|
|
## Mem0 Session Bootstrap
|
|
|
|
You have access to persistent memory via the mem0 MCP tools. Before doing anything else:
|
|
|
|
1. Call `search_memories` with a query related to the current project or user request to load relevant context.
|
|
2. Review the returned memories to understand what has been learned in prior sessions.
|
|
3. If appropriate, call `get_memories` to browse all stored memories for this user.
|
|
|
|
IMPORTANT: Do NOT skip this step. Always bootstrap context first.
|
|
EOF
|
|
|
|
elif [ "$SOURCE" = "resume" ]; then
|
|
cat <<'EOF'
|
|
## Mem0 Session Resumed
|
|
|
|
This is a resumed session. Your prior context is already loaded. Before continuing:
|
|
|
|
1. Call `search_memories` with a query related to the current task to refresh relevant memories.
|
|
2. If significant time has passed, search for recent project-wide updates.
|
|
|
|
Continue where you left off.
|
|
EOF
|
|
|
|
elif [ "$SOURCE" = "compact" ]; then
|
|
cat <<'EOF'
|
|
## Mem0 Post-Compaction Recovery
|
|
|
|
Context was just compacted. You may have lost important session context.
|
|
|
|
1. Call `search_memories` with queries related to what you were working on to reload relevant knowledge.
|
|
2. Check for any session state memories that were saved before compaction.
|
|
3. Continue working based on the recovered context.
|
|
EOF
|
|
fi
|
|
|
|
exit 0
|