feat: Add memory reminders to improve memory use (#366)

This commit is contained in:
Devansh Jain
2025-12-23 12:00:34 -08:00
committed by GitHub
parent ef09af92f2
commit 54f0f233b3
6 changed files with 133 additions and 3 deletions

View File

@@ -9,6 +9,7 @@ import lettaAnthropicPrompt from "./prompts/letta_claude.md";
import lettaCodexPrompt from "./prompts/letta_codex.md";
import lettaGeminiPrompt from "./prompts/letta_gemini.md";
import loadedSkillsPrompt from "./prompts/loaded_skills.mdx";
import memoryCheckReminder from "./prompts/memory_check_reminder.txt";
import personaPrompt from "./prompts/persona.mdx";
import personaClaudePrompt from "./prompts/persona_claude.mdx";
import personaKawaiiPrompt from "./prompts/persona_kawaii.mdx";
@@ -26,6 +27,7 @@ export const PLAN_MODE_REMINDER = planModeReminder;
export const SKILL_UNLOAD_REMINDER = skillUnloadReminder;
export const SKILL_CREATOR_PROMPT = skillCreatorModePrompt;
export const REMEMBER_PROMPT = rememberPrompt;
export const MEMORY_CHECK_REMINDER = memoryCheckReminder;
export const MEMORY_PROMPTS: Record<string, string> = {
"persona.mdx": personaPrompt,

View File

@@ -0,0 +1,10 @@
<system-reminder>
MEMORY CHECK: Review this conversation for information worth storing in your memory blocks. Update memory silently (no confirmation needed) if you learned:
- **User info**: Name, role, preferences, working style, current work/goals
- **Project details**: Architecture, patterns, gotchas, dependencies, conventions, workflow rules
- **Corrections**: User corrected you or clarified something important
- **Preferences**: How they want you to behave, communicate, or approach tasks
Ask yourself: "If I started a new session tomorrow, what from this conversation would I want to remember?" If the answer is meaningful, update the appropriate memory block(s) now.
</system-reminder>

View File

@@ -89,6 +89,10 @@ import {
} from "./helpers/accumulator";
import { backfillBuffers } from "./helpers/backfill";
import { formatErrorDetails } from "./helpers/errorFormatter";
import {
buildMemoryReminder,
parseMemoryPreference,
} from "./helpers/memoryReminder";
import {
buildMessageContentFromDisplay,
clearPlaceholdersInText,
@@ -505,6 +509,9 @@ export default function App({
// Track if we've sent the session context for this CLI session
const hasSentSessionContextRef = useRef(false);
// Track conversation turn count for periodic memory reminders
const turnCountRef = useRef(0);
// Static items (things that are done rendering and can be frozen)
const [staticItems, setStaticItems] = useState<StaticItem[]>([]);
@@ -1674,6 +1681,9 @@ export default function App({
setStaticItems([]);
setStaticRenderEpoch((e) => e + 1);
// Reset turn counter for memory reminders when switching agents
turnCountRef.current = 0;
// Update agent state - also update ref immediately for any code that runs before re-render
agentIdRef.current = targetAgentId;
setAgentId(targetAgentId);
@@ -2287,6 +2297,9 @@ export default function App({
// emittedIdsRef.current.clear();
// setStaticItems([]);
// Reset turn counter for memory reminders
turnCountRef.current = 0;
// Update command with success
buffersRef.current.byId.set(cmdId, {
kind: "command",
@@ -3246,12 +3259,21 @@ DO NOT respond to these messages or otherwise consider them in your response unl
bashCommandCacheRef.current = [];
}
// Combine reminders with content (session context first, then plan mode, then skill unload, then bash commands)
// Build memory reminder if interval is set and we've reached the Nth turn
const memoryReminderContent = await buildMemoryReminder(
turnCountRef.current,
);
// Increment turn count for next iteration
turnCountRef.current += 1;
// Combine reminders with content (session context first, then plan mode, then skill unload, then bash commands, then memory reminder)
const allReminders =
sessionContextReminder +
planModeReminder +
skillUnloadReminder +
bashCommandPrefix;
bashCommandPrefix +
memoryReminderContent;
const messageContent =
allReminders && typeof contentParts === "string"
? allReminders + contentParts
@@ -4476,6 +4498,9 @@ DO NOT respond to these messages or otherwise consider them in your response unl
// Get questions from approval args
const questions = getQuestionsFromApproval(approval);
// Check for memory preference question and update setting
parseMemoryPreference(questions, answers);
// Format the answer string like Claude Code does
const answerParts = questions.map((q) => {
const answer = answers[q.question] || "";

View File

@@ -0,0 +1,90 @@
// src/cli/helpers/memoryReminder.ts
// Handles periodic memory reminder logic and preference parsing
import { settingsManager } from "../../settings-manager";
// Memory reminder interval presets
const MEMORY_INTERVAL_FREQUENT = 3;
const MEMORY_INTERVAL_OCCASIONAL = 8;
/**
* Get the effective memory reminder interval (local setting takes precedence over global)
* @returns The memory interval setting, or null if disabled
*/
function getMemoryInterval(): number | null {
// Check local settings first (may not be loaded, so catch errors)
try {
const localSettings = settingsManager.getLocalProjectSettings();
if (localSettings.memoryReminderInterval !== undefined) {
return localSettings.memoryReminderInterval;
}
} catch {
// Local settings not loaded, fall through to global
}
// Fall back to global setting
return settingsManager.getSetting("memoryReminderInterval");
}
/**
* Build a memory check reminder if the turn count matches the interval
* @param turnCount - Current conversation turn count
* @returns Promise resolving to the reminder string (empty if not applicable)
*/
export async function buildMemoryReminder(turnCount: number): Promise<string> {
const memoryInterval = getMemoryInterval();
if (memoryInterval && turnCount > 0 && turnCount % memoryInterval === 0) {
const { MEMORY_CHECK_REMINDER } = await import(
"../../agent/promptAssets.js"
);
return MEMORY_CHECK_REMINDER;
}
return "";
}
interface Question {
question: string;
header?: string;
}
/**
* Parse user's answer to a memory preference question and update settings
* @param questions - Array of questions that were asked
* @param answers - Record of question -> answer
* @returns true if a memory preference was detected and setting was updated
*/
export function parseMemoryPreference(
questions: Question[],
answers: Record<string, string>,
): boolean {
for (const q of questions) {
const questionLower = q.question.toLowerCase();
const headerLower = q.header?.toLowerCase() || "";
// Match memory-related questions
if (
questionLower.includes("memory") ||
questionLower.includes("remember") ||
headerLower.includes("memory")
) {
const answer = answers[q.question]?.toLowerCase() || "";
// Parse answer: "frequent" → MEMORY_INTERVAL_FREQUENT, "occasional" → MEMORY_INTERVAL_OCCASIONAL
if (answer.includes("frequent")) {
settingsManager.updateLocalProjectSettings({
memoryReminderInterval: MEMORY_INTERVAL_FREQUENT,
});
return true;
} else if (answer.includes("occasional")) {
settingsManager.updateLocalProjectSettings({
memoryReminderInterval: MEMORY_INTERVAL_OCCASIONAL,
});
return true;
}
break; // Only process first matching question
}
}
return false;
}

View File

@@ -11,6 +11,7 @@ export interface Settings {
tokenStreaming: boolean;
enableSleeptime: boolean;
sessionContextEnabled: boolean; // Send device/agent context on first message of each session
memoryReminderInterval: number | null; // null = disabled, number = prompt memory check every N turns
globalSharedBlockIds: Record<string, string>; // DEPRECATED: kept for backwards compat
profiles?: Record<string, string>; // DEPRECATED: old format, kept for migration
pinnedAgents?: string[]; // Array of agent IDs pinned globally
@@ -47,6 +48,7 @@ export interface LocalProjectSettings {
permissions?: PermissionRules;
profiles?: Record<string, string>; // DEPRECATED: old format, kept for migration
pinnedAgents?: string[]; // Array of agent IDs pinned locally
memoryReminderInterval?: number | null; // null = disabled, number = overrides global
}
const DEFAULT_SETTINGS: Settings = {
@@ -54,6 +56,7 @@ const DEFAULT_SETTINGS: Settings = {
tokenStreaming: false,
enableSleeptime: false,
sessionContextEnabled: true,
memoryReminderInterval: 1, // number = prompt memory check every N turns
globalSharedBlockIds: {},
};

View File

@@ -215,7 +215,7 @@ You should ask these questions at the start (bundle them together in one AskUser
1. **Research depth**: "Standard or deep research (comprehensive, as long as needed)?"
2. **Identity**: "Which contributor are you?" (You can often infer this from git logs - e.g., if git shows "cpacker" as a top contributor, ask "Are you cpacker?")
3. **Related repos**: "Are there other repositories I should know about and consider in my research?" (e.g., backend monorepo, shared libraries)
4. **Workflow style**: "How proactive should I be?" (auto-commit vs ask-first)
4. **Memory updates**: "How often should I check if I should update my memory?" with options "Frequent (every 3-5 turns)" and "Occasional (every 8-10 turns)". This should be a binary question with "Memory" as the header.
5. **Communication style**: "Terse or detailed responses?"
6. **Any specific rules**: "Rules I should always follow?"