The Memory Protocol is a set of behavioral rules that teaches AI agents when and how to use Engram’s memory tools. Without it, agents have the tools but no guidance on when to call them.The protocol is injected via:
System prompt (OpenCode, Gemini CLI, Codex, VS Code)
Skill file (Claude Code)
Agent rules (Cursor, Windsurf, Antigravity)
The protocol is enforced as mandatory — not optional. This ensures memory continuity across sessions and context resets.
Every saved observation must follow this structure:
{ title: "Chose Zustand over Redux for state", type: "decision", scope: "project", topic_key: "architecture/state-management", content: `**What**: Replaced Redux with Zustand for state management**Why**: Redux boilerplate (actions, reducers, connect) was slowing feature velocity. Team wanted something simpler but still debuggable.**Where**: - src/store/ (deleted entire Redux setup)- src/hooks/useAppStore.ts (new Zustand store)- src/components/**/*.tsx (updated imports)**Learned**: - Zustand devtools require wrapping create() with devtools() — not automatic- Zustand doesn't have built-in async middleware — use immer for complex updates- Migration took 3 hours, removed 400 lines of boilerplate `}
## Goal[What we were working on this session — one paragraph]## Instructions[User preferences or constraints discovered during this session — skip if none]## Discoveries- [Technical findings, gotchas, non-obvious learnings]- [Each discovery should be actionable or surprising]## Accomplished- [Completed items with key details — not just "fixed bug", but "fixed N+1 query in UserList by adding eager loading"]## Next Steps- [What remains to be done — for the next session]- [Any blockers or decisions pending]## Relevant Files- path/to/file.ts — [what it does or what changed]- another/file.go — [brief description]
Example: Feature Work
Example: Bugfix Session
## GoalImplement JWT authentication to replace the existing session-based auth system. User wanted stateless auth that works across multiple server instances.## Instructions- User prefers httpOnly cookies over localStorage for XSS protection- Team coding style: async/await over .then() chains- All auth code should live in src/auth/ directory## Discoveries- express-jwt middleware doesn't handle refresh tokens — had to implement custom rotation logic- bcrypt cost factor of 12 is the sweet spot for our server (10 was too fast, 14 too slow)- JWT tokens must include user role for RBAC — can't just store user ID## Accomplished- Migrated from express-session to jsonwebtoken (removed 200 lines of session config)- Implemented refresh token rotation with atomic updates to prevent race conditions- Added JWT middleware to all protected routes in src/routes/- Updated login/logout endpoints to issue/revoke tokens- Tested with 1000 concurrent requests — no token collisions## Next Steps- Add rate limiting to login endpoint (user mentioned this but ran out of time)- Write integration tests for token refresh flow- Update API documentation with new auth headers## Relevant Files- src/auth/jwt.ts — JWT signing, verification, refresh token logic- src/middleware/authenticate.ts — Middleware to verify tokens on protected routes- src/routes/auth.ts — Login, logout, refresh endpoints- src/models/RefreshToken.ts — Database model for refresh token tracking
## GoalFix the N+1 query problem in the user list endpoint that was causing 2-3 second page loads.## Discoveries- Sequelize doesn't eager-load relationships by default — must explicitly .include()- Enabling Sequelize query logging (logging: console.log) helped identify the issue- The bug was introduced in commit a3f8c1d when we added the posts relationship but forgot to update the query## Accomplished- Added eager loading for user.posts in UserController.index()- Page load time dropped from 2.3s to 180ms (tested with 150 users)- Added a comment in the code explaining why .include() is necessary## Next Steps- Audit other endpoints for similar N+1 patterns## Relevant Files- src/controllers/UserController.ts — Added .include('posts') to index query- src/models/User.ts — Verified posts relationship definition
The session summary is stored as a special observation with type: "session_summary" and is automatically injected into the next session’s context.
// You receive this in context:// "FIRST ACTION REQUIRED: Call mem_session_summary with the following content..."// Step 1: Persist the compacted workawait mem_session_summary({ session_id: currentSessionId, content: `[compacted summary from the message]`});// Step 2: Recover contextconst context = await mem_context({ project: currentProject });// Step 3: Now you can continue// You have both the compacted work AND previous session context
When completing a task, you can include a ## Key Learnings: section at the end of your response:
## Key Learnings:1. bcrypt cost=12 is the right balance for our server performance2. JWT refresh tokens need atomic rotation to prevent race conditions3. express-jwt doesn't handle refresh logic — must implement custom middleware
Engram will automatically extract and save these as observations with type: "learning".
This is a safety net — it captures knowledge even if you forget to call mem_save explicitly. But explicit mem_save calls are still preferred for important work.
For agents without plugin support, add this to your agent’s system prompt or rules file:
Memory Protocol (Full Text)
## Memory Protocol### WHEN TO SAVE (mandatory — not optional)Call `mem_save` IMMEDIATELY after any of these:- Bug fix completed- Architecture or design decision made- Non-obvious discovery about the codebase- Configuration change or environment setup- Pattern established (naming, structure, convention)- User preference or constraint learnedFormat for `mem_save`:- **title**: Verb + what — short, searchable (e.g. "Fixed N+1 query in UserList", "Chose Zustand over Redux")- **type**: `bugfix` | `decision` | `architecture` | `discovery` | `pattern` | `config` | `preference`- **scope**: `project` (default) | `personal`- **topic_key** (optional, recommended for evolving decisions): stable key like `architecture/auth-model`- **content**:
What: One sentence — what was done
Why: What motivated it (user request, bug, performance, etc.)
Where: Files or paths affected
Learned: Gotchas, edge cases, things that surprised you (omit if none)
### Topic update rules (mandatory)- Different topics must not overwrite each other (e.g. architecture vs bugfix)- Reuse the same `topic_key` to update an evolving topic instead of creating new observations- If unsure about the key, call `mem_suggest_topic_key` first and then reuse it- Use `mem_update` when you have an exact observation ID to correct### WHEN TO SEARCH MEMORYWhen the user asks to recall something — any variation of "remember", "recall", "what did we do", "how did we solve", "recordar", "acordate", "qué hicimos", or references to past work:1. First call `mem_context` — checks recent session history (fast, cheap)2. If not found, call `mem_search` with relevant keywords (FTS5 full-text search)3. If you find a match, use `mem_get_observation` for full untruncated contentAlso search memory PROACTIVELY when:- Starting work on something that might have been done before- The user mentions a topic you have no context on — check if past sessions covered it### SESSION CLOSE PROTOCOL (mandatory)Before ending a session or saying "done" / "listo" / "that's it", you MUST call `mem_session_summary` with this structure:
This is NOT optional. If you skip this, the next session starts blind.### AFTER COMPACTIONIf you see a message about compaction or context reset, or if you see "FIRST ACTION REQUIRED" in your context:1. IMMEDIATELY call `mem_session_summary` with the compacted summary content — this persists what was done before compaction2. Then call `mem_context` to recover any additional context from previous sessions3. Only THEN continue workingDo not skip step 1. Without it, everything done before compaction is lost from memory.