Skip to main content

Overview

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.

When to Save (Mandatory)

Call mem_save IMMEDIATELY after any of these:

Bug Fix Completed

Save what was wrong, why it broke, how you fixed it, and any gotchas.

Architecture/Design Decision

Save the decision, tradeoffs considered, and why this choice was made.

Non-Obvious Discovery

Save anything surprising about the codebase that future sessions should know.

Configuration Change

Save environment setup, config changes, or tool installations.

Pattern Established

Save naming conventions, code structure patterns, or team standards.

User Preference Learned

Save user constraints, preferences, or coding style requirements.

Format for mem_save

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
  `
}

Content Structure Rules

1

What

One sentence — concise description of what was done.
2

Why

What motivated this? User request, bug, performance issue, tech debt?
3

Where

File paths — which files were affected. Format: src/path/file.ts — description
4

Learned

Gotchas, edge cases, things that surprised you. Omit if none — don’t force this section.
Title should be short and searchable — like a commit message. Bad: “Fixed a bug”. Good: “Fixed N+1 query in user list”.

When to Search Memory

Reactive Search (User Asks)

When the user asks to recall something — any variation of:
  • “remember”
  • “recall”
  • “what did we do”
  • “how did we solve”
  • “recordar” (Spanish)
  • “acordate” (Spanish)
  • “qué hicimos” (Spanish)
Protocol:
  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 content
// Step 1: Check recent context
const context = await mem_context({ project: currentProject });

// Step 2: If not in recent sessions, search
if (!foundInContext) {
  const results = await mem_search({
    query: "auth bug fix",
    type: "bugfix",
    limit: 5
  });
}

// Step 3: Get full content if needed
if (results[0].id) {
  const full = await mem_get_observation({ id: results[0].id });
}

Proactive Search (Overlap Detection)

Search memory BEFORE starting work when:
  • Starting work on something that might have been done before
  • User mentions a topic you have no context on — check if past sessions covered it
  • About to implement a pattern — check if the team has a preferred approach
const authDecisions = await mem_search({
  query: "JWT authentication",
  type: "decision",
  scope: "project"
});

if (authDecisions.length > 0) {
  // Found existing decision — follow that pattern
} else {
  // New territory — proceed and save the decision
}
Proactive search prevents duplicate work and ensures consistency with past decisions.

Topic Update Rules (Mandatory)

When saving evolving topics (architecture, long-running features), follow these rules:
Use distinct topic keys for different concerns:
  • architecture/auth-modelbug/auth-nil-panic
  • decision/state-managementpattern/component-structure
Bad: Using auth for both architecture and bugfixes — they’ll overwrite each other.
When a decision changes over time, use the same topic key to update:
// First save
mem_save({
  title: "Auth architecture",
  topic_key: "architecture/auth-model",
  content: "Using JWT with httpOnly cookies"
});
// Creates observation ID 100, revision_count=1

// Later evolution
mem_save({
  title: "Auth architecture",
  topic_key: "architecture/auth-model",
  content: "Switched to refresh token rotation"
});
// UPDATES observation ID 100, revision_count=2
If you’re not sure what topic key to use:
const suggestion = await mem_suggest_topic_key({
  type: "architecture",
  title: "Auth token strategy"
});
// Returns: "architecture-auth-token-strategy"

// Then reuse it for future updates
await mem_save({
  topic_key: suggestion.topic_key,
  ...
});
If you know the observation ID and want to correct it:
await mem_update({
  id: 42,
  content: "Updated content with new details"
});
This does not increment revision_count — it’s a correction, not an evolution.

Session Close Protocol (Mandatory)

This is NOT optional. Before ending a session or saying “done” / “listo” / “that’s it”, you MUST call mem_session_summary.
Without this, the next session starts blind.

Session Summary Format

## 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]
## Goal
Implement 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
The session summary is stored as a special observation with type: "session_summary" and is automatically injected into the next session’s context.

After Compaction (Critical)

When you see a message about compaction or context reset, or if you see “FIRST ACTION REQUIRED” in your context:
1

Persist compacted work

IMMEDIATELY call mem_session_summary with the compacted summary content.This persists everything done before compaction.
2

Recover context

Call mem_context to load context from previous sessions.
3

Continue working

Only then continue with the user’s request.
Do not skip step 1. Without it, everything done before compaction is lost from memory forever.

Compaction Example

// You receive this in context:
// "FIRST ACTION REQUIRED: Call mem_session_summary with the following content..."

// Step 1: Persist the compacted work
await mem_session_summary({
  session_id: currentSessionId,
  content: `[compacted summary from the message]`
});

// Step 2: Recover context
const context = await mem_context({ project: currentProject });

// Step 3: Now you can continue
// You have both the compacted work AND previous session context

Passive Capture (Optional)

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 performance
2. JWT refresh tokens need atomic rotation to prevent race conditions
3. 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.

Scope: Project vs Personal

Observations can be scoped:
Project scope — shared with the team, relevant to this codebase.Use for:
  • Architecture decisions
  • Bugfixes
  • Code patterns
  • Team conventions
mem_save({
  title: "Auth architecture",
  scope: "project", // or omit (defaults to project)
  ...
});
When searching with mem_search, you can filter by scope: mem_search({ query: "...", scope: "project" }) or scope: "personal".

Privacy: The <private> Tag

Wrap sensitive content in <private> tags to strip it before storage:
mem_save({
  title: "OpenAI API setup",
  content: `
**What**: Configured OpenAI SDK with API key
**Where**: src/config/openai.ts
**Key**: <private>sk-abc123xyz456</private>
  `
});

// Stored as:
// **Key**: [REDACTED]
Privacy stripping happens at two layers (plugin + store). Even if the plugin fails, the store layer catches it.

Memory Protocol Checklist

Use this checklist to verify protocol compliance:
  • Called mem_save after each significant action (bugfix, decision, discovery)
  • Used structured What/Why/Where/Learned format for content
  • Short, searchable titles (not “fixed bug”, but “fixed N+1 in UserList”)
  • Used topic_key for evolving decisions
  • Searched memory proactively before starting overlapping work
  • Called mem_session_summary with Goal/Discoveries/Accomplished/Files format
  • Summary includes ALL significant work, not just the last task
  • Listed relevant files with descriptions
  • Added Next Steps for future sessions
  • Called mem_session_summary to persist compacted work (FIRST ACTION)
  • Called mem_context to recover previous session context
  • Only then continued with user’s request

Full Protocol Text (For Copy-Paste)

For agents without plugin support, add this to your agent’s system prompt or rules file:
## 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 learned

Format 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 MEMORY

When 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 content

Also 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:

Goal

[What we were working on this session]

Instructions

[User preferences or constraints discovered — skip if none]

Discoveries

  • [Technical findings, gotchas, non-obvious learnings]

Accomplished

  • [Completed items with key details]

Next Steps

  • [What remains to be done — for the next session]

Relevant Files

  • path/to/file — [what it does or what changed]

This is NOT optional. If you skip this, the next session starts blind.

### AFTER COMPACTION

If 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 compaction
2. Then call `mem_context` to recover any additional context from previous sessions
3. Only THEN continue working

Do not skip step 1. Without it, everything done before compaction is lost from memory.

Next Steps

How It Works

Memory system, session lifecycle, and 3-layer pattern

Architecture

System architecture, components, and data flow

MCP Tools

Complete reference for all 13 memory tools

Agent Setup

Set up Engram with your agent (OpenCode, Claude Code, etc.)

Build docs developers (and LLMs) love