refactor(plugin): hand mem0 search decisions to the agent

The on_user_prompt hook previously ran a blind semantic search using the
raw user prompt as the query and injected the top results unconditionally.
That undermined the agent's judgment with low-quality context and made
heavy API calls on prompts where memory wouldn't help.

Replace with a decision rubric injected into context per turn. The agent
decides whether memory would improve the response and, if so, runs targeted
searches with metadata.type filters and proper query phrasing. Mirror the
same rules in mem0-codex/SKILL.md so the skill (loaded once) and the hook
(re-asserted per turn) agree.

Skip gates preserved: prompts under 20 chars and missing MEM0_API_KEY.

- on_user_prompt.sh: API call replaced with stdout rubric injection
- hooks.json / codex-hooks.json: statusMessage updated to match new behavior
- mem0-codex/SKILL.md: "On every new task" rewritten with search/skip rules,
  query phrasing guidance, metadata filter table, and a worked example
This commit is contained in:
gabrielstein-mem0
2026-04-27 13:58:34 -07:00
parent 30ce028a71
commit 19c1956630
4 changed files with 107 additions and 45 deletions
+1 -1
View File
@@ -18,7 +18,7 @@
{
"type": "command",
"command": "${CODEX_PLUGIN_ROOT}/scripts/on_user_prompt.sh",
"statusMessage": "Searching mem0 memories...",
"statusMessage": "Checking memory relevance...",
"timeout": 5
}
]
+1 -1
View File
@@ -57,7 +57,7 @@
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_user_prompt.sh",
"statusMessage": "Searching mem0 memories...",
"statusMessage": "Checking memory relevance...",
"timeout": 5
}
]
+38 -36
View File
@@ -1,61 +1,63 @@
#!/usr/bin/env bash
# Hook: UserPromptSubmit
#
# Fires on every user message. Searches mem0 for relevant memories
# and injects them into Claude's context before processing.
# Fires on every user message. Instead of pre-searching mem0 with the
# raw prompt, this injects a decision rubric telling the agent when
# and how to search itself. The agent has more context than this
# script does -- let it decide.
#
# Input: JSON on stdin with prompt, session_id, cwd, transcript_path
# Output: Matching memories as context text (exit 0)
#
# Skips search for very short prompts (< 20 chars) and when
# MEM0_API_KEY is not set. Uses a 3s timeout to minimize latency.
# Input: JSON on stdin (prompt, session_id, cwd, transcript_path)
# Output: Decision rubric injected into Claude's context (exit 0)
# Intentionally omit -e so the script always exits 0 even if
# curl or jq fail — must never block the user's prompt.
# Intentionally omit -e so the script always exits 0 even if jq fails --
# must never block the user's prompt.
set -uo pipefail
INPUT=$(cat)
PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""' 2>/dev/null || echo "")
# Skip trivial prompts — not worth a network call
# Acknowledgements and short replies don't warrant memory context
if [ ${#PROMPT} -lt 20 ]; then
exit 0
fi
API_KEY="${MEM0_API_KEY:-}"
if [ -z "$API_KEY" ]; then
# No API key means the agent can't search anyway
if [ -z "${MEM0_API_KEY:-}" ]; then
exit 0
fi
USER_ID="${MEM0_USER_ID:-${USER:-default}}"
# Build request body safely via jq to avoid injection
BODY=$(jq -n --arg query "$PROMPT" --arg user_id "$USER_ID" \
'{query: $query, filters: {user_id: $user_id}, top_k: 5}')
cat <<EOF
## Memory check
# Search mem0 for memories relevant to this prompt
RESPONSE=$(curl -s --max-time 3 \
-X POST "https://api.mem0.ai/v2/memories/search/" \
-H "Authorization: Token $API_KEY" \
-H "Content-Type: application/json" \
-d "$BODY" \
2>/dev/null || echo "")
Before responding, decide whether persistent memory context from mem0 would
improve your answer. The agent -- not this hook -- owns this decision.
if [ -z "$RESPONSE" ]; then
exit 0
fi
**Search WHEN** the user:
- references past work, decisions, or things "we" built
- asks "how should we...", "best way to...", or any decision-style question
- hits an error, bug, or asks for debugging help
- requests work that touches their stack, tools, conventions, or preferences
- starts a non-trivial task in a known project
# Extract memories from response (API returns a flat array)
MEMORIES=$(echo "$RESPONSE" | jq -r '
if type == "array" then . else .results // [] end |
if length == 0 then empty else
"## Relevant memories from mem0\n\n" +
(map(select(.memory != null) | "- " + .memory) | join("\n"))
end
' 2>/dev/null || echo "")
**Skip WHEN:**
- the prompt is an acknowledgement or continuation
- the user is *stating* new info -- that's a write trigger (\`add_memory\`), not a search
- it's a pure syntax / factual question answerable from general knowledge
- you already searched this scope earlier in the turn
if [ -n "$MEMORIES" ]; then
echo "$MEMORIES"
fi
**If searching, do it well:**
- Run **2-4 parallel** \`search_memories\` calls with different angles, not one
query that echoes the user's prompt.
- Phrase queries as **nouns** ("auth module decisions"), not full sentences.
- Use \`metadata.type\` filters where they fit:
- \`{"metadata.type": "decision"}\` -- design / architecture questions
- \`{"metadata.type": "anti_pattern"}\` -- debugging, error handling
- \`{"metadata.type": "user_preference"}\` -- tooling, stack, style
- \`{"metadata.type": "convention"}\` -- established patterns in this project
- Scope every query with \`{"user_id": "$USER_ID"}\`.
- Empty results are normal -- proceed without context.
EOF
exit 0
+67 -7
View File
@@ -1,10 +1,10 @@
---
name: mem0-codex
description: >
Mem0 persistent memory integration for Codex. Automatically retrieve relevant
memories at the start of each task, store key learnings when tasks complete,
and capture session state before context is lost. Use the mem0 MCP tools
(add_memory, search_memories, get_memories, etc.) for all memory operations.
Mem0 persistent memory integration for Codex. Decide deliberately when memory
context would help, run targeted searches with metadata filters when it would,
and store key learnings as work completes. Use the mem0 MCP tools (add_memory,
search_memories, get_memories, etc.) for all memory operations.
---
# Mem0 Memory Protocol for Codex
@@ -13,9 +13,69 @@ You have access to persistent memory via the mem0 MCP tools. Follow this protoco
## On every new task
1. Call `search_memories` with a query related to the current task or project to load relevant context.
2. Review 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.
Decide whether persistent memory context would improve your response, then act accordingly. Don't search by default — search deliberately.
### Decide: search or skip?
**Search WHEN** the user:
- references past work, decisions, or things "we" built
- asks "how should we...", "best way to...", or any decision-style question
- hits an error, bug, or asks for debugging help
- requests work that touches their stack, tools, conventions, or preferences
- starts a non-trivial task in a known project
**Skip WHEN:**
- the prompt is an acknowledgement or continuation ("ok", "thanks", "continue")
- the user is *stating* new info — that's a write trigger (`add_memory`), not a search
- it's a pure syntax / factual question answerable from general knowledge
- you already searched this scope earlier in the turn
Empty results are normal. Proceed without context — they don't mean the system is broken.
### How to search well
When you do search, run **2–4 parallel** `search_memories` calls at different angles instead of one query echoing the user's prompt.
**Query phrasing:**
- Use **nouns**, not sentences. `"auth module decisions"` beats `"what did we decide about auth"`.
- Strip conversational filler. *"remember when we picked Postgres?"* → search `"Postgres choice"`.
- Use entity names, not pronouns. Resolve "that thing" from recent context first.
- Don't search on meta-questions ("what was that?") — use recent context or `get_memories` ordered by `created_at`.
**Metadata filters** match the same `metadata.type` values written under "After completing significant work" below:
| Filter | Use for |
|--------|---------|
| `{"metadata.type": "decision"}` | design / architecture / "how should we" questions |
| `{"metadata.type": "anti_pattern"}` | debugging, error handling, things that failed before |
| `{"metadata.type": "user_preference"}` | tooling, stack, style — always include for code work |
| `{"metadata.type": "convention"}` | established patterns in this project |
Combine with `user_id` using AND:
```python
filters={"AND": [{"user_id": "alice"}, {"metadata.type": "decision"}]}
```
### Worked example
User asks: *"Refactor the auth module to use JWT."*
Don't:
```python
search_memories(query="Refactor the auth module to use JWT")
# Hits whatever shares words. Misses prior decisions and preferences.
```
Do (parallel):
```python
search_memories(query="auth module decisions",
filters={"AND": [{"user_id": "alice"}, {"metadata.type": "decision"}]})
search_memories(query="JWT", filters={"user_id": "alice"})
search_memories(query="auth refactor failures",
filters={"AND": [{"user_id": "alice"}, {"metadata.type": "anti_pattern"}]})
search_memories(query="auth",
filters={"AND": [{"user_id": "alice"}, {"metadata.type": "user_preference"}]})
```
## After completing significant work