forget
Delete memories by ID or trigger auto-forgetting based on importance, heat, and age. Preserves caveat, goal, and pinned memories to avoid accidental loss.
Instructions
Explicitly delete a memory by id, OR run auto-forgetting across all memories based on forgettingRisk (importance + heat + age). Caveat-layer, goal-layer, and pinned (importance>=0.9) memories are always preserved. Prefer update_memory for corrections — forget is destructive.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| memory_id | No | ||
| dry_run | No | Report what would be deleted without actually deleting. |
Implementation Reference
- src/mcp/server.ts:496-563 (handler)handleForget function: the main handler for the 'forget' tool. Handles explicit memory deletion by ID (with protection checks for protected/pinned memories) and auto-sweep across all non-protected memories with importance < 0.9, using decideForgetting() to determine which memories to drop. Runs SQL DELETE in a transaction for auto-sweep.
function handleForget(args: any): string { if (args.memory_id) { // Explicit delete by id — respect protected AND pinned (importance >= 0.9) const target = db.prepare('SELECT id, layer, importance, protected FROM memories WHERE id = ?').get(args.memory_id) as any; if (!target) { return JSON.stringify({ ok: false, error: `memory_id ${args.memory_id} not found` }); } if (target.protected === 1 || target.importance >= 0.9) { const isLayerProtected = target.protected === 1; return JSON.stringify({ ok: false, preserved: true, reason: isLayerProtected ? `${target.layer}-layer is auto-protected` : 'pinned (importance>=0.9)', hint: isLayerProtected ? `${target.layer} memories are permanently protected (the whole point — pain lessons must not be lost). If you truly need to delete, copy its content to another layer via remember() first, then drop the DB row manually via a SQLite client.` : 'Use update_memory to lower importance below 0.9 first, then forget.', }); } const res = db.prepare('DELETE FROM memories WHERE id = ?').run(args.memory_id); return JSON.stringify({ ok: true, deleted: res.changes, memory_id: args.memory_id }); } // Auto-sweep — also respect pin (importance >= 0.9) as protection const rows = db .prepare(`SELECT id, layer, importance, access_count, last_accessed_at, protected FROM memories WHERE protected = 0 AND importance < 0.9`) .all() as any[]; const now = Math.floor(Date.now() / 1000); const actions: { id: number; action: string }[] = []; for (const r of rows) { const daysSince = (now - r.last_accessed_at) / 86400; const heat = computeHeat({ accessesLast30d: daysSince < 30 ? r.access_count : 0, accessesLast90d: daysSince < 90 ? r.access_count : 0, daysSinceLastAccess: daysSince, totalAccesses: r.access_count, baseImportance: r.importance, }); const action = decideForgetting({ daysSinceLastAccess: daysSince, importance: r.importance, heatScore: heat.score, protected: r.protected === 1, layer: r.layer, }); if (action !== 'keep') actions.push({ id: r.id, action }); } const toDropIds = actions.filter((a) => a.action === 'drop').map((a) => a.id); if (!args.dry_run) { const del = db.prepare('DELETE FROM memories WHERE id = ?'); const tx = db.transaction((ids: number[]) => { for (const id of ids) del.run(id); }); tx(toDropIds); } return JSON.stringify({ ok: true, dry_run: !!args.dry_run, scanned: rows.length, to_drop: toDropIds.length, to_compress: actions.filter((a) => a.action === 'compress').length, sample_ids_to_drop: toDropIds.slice(0, 10), }); } - src/mcp/server.ts:130-140 (registration)Tool registration entry for 'forget' in the TOOLS array. Describes the tool for explicit delete by memory_id or auto-forgetting based on forgettingRisk. Input schema accepts optional memory_id (number) and dry_run (boolean).
name: 'forget', description: 'Explicitly delete a memory by id, OR run auto-forgetting across all memories based on forgettingRisk (importance + heat + age). Caveat-layer, goal-layer, and pinned (importance>=0.9) memories are always preserved. Prefer update_memory for corrections — forget is destructive.', inputSchema: { type: 'object', properties: { memory_id: { type: 'number' }, dry_run: { type: 'boolean', default: false, description: 'Report what would be deleted without actually deleting.' }, }, }, }, - src/mcp/server.ts:130-140 (schema)Input schema for the 'forget' tool: memory_id (number, optional) and dry_run (boolean, default false).
name: 'forget', description: 'Explicitly delete a memory by id, OR run auto-forgetting across all memories based on forgettingRisk (importance + heat + age). Caveat-layer, goal-layer, and pinned (importance>=0.9) memories are always preserved. Prefer update_memory for corrections — forget is destructive.', inputSchema: { type: 'object', properties: { memory_id: { type: 'number' }, dry_run: { type: 'boolean', default: false, description: 'Report what would be deleted without actually deleting.' }, }, }, }, - src/lib/forgetting.ts:1-42 (helper)Forgetting curve helper library: defines ForgettingInput interface, forgettingRisk() function (Ebbinghaus-based calculation with heat/importance/time factors), COMPRESS_THRESHOLD (50) and DROP_THRESHOLD (200) constants, and decideForgetting() which returns 'keep', 'compress', or 'drop'.
// Ported from linksee-app/setup-learning-box.cjs:62-99 (Ebbinghaus forgetting curve). // Implements Michie's memory principle 6: active forgetting. // Protected memories (caveat layer) and goal layer bypass decay. export interface ForgettingInput { daysSinceLastAccess: number; importance: number; // 0.0-1.0 heatScore: number; // 0-100 protected: boolean; layer: string; } // Returns true if this memory should be forgotten (compressed to summary or deleted). // Higher forgettingRisk → more likely to forget. export function forgettingRisk(input: ForgettingInput): number { if (input.protected) return 0; if (input.layer === 'goal') return 0; // Goals are WHY-anchors, never auto-forget while active // Original formula from setup-learning-box.cjs: // daysSinceContact * (heatScore/100) * (1 + daysSinceContact/30) // We INVERT: high heat = low risk (hot memories should be kept). const heatFactor = 1 - (input.heatScore / 100); // 0.0 = keep, 1.0 = drop const importanceFactor = 1 - input.importance; const timeFactor = input.daysSinceLastAccess * (1 + input.daysSinceLastAccess / 30); return heatFactor * importanceFactor * timeFactor; } // Risk threshold above which memory is compressed (→ learning layer summary) and the original deleted. export const COMPRESS_THRESHOLD = 50; // Risk threshold above which memory is entirely dropped. export const DROP_THRESHOLD = 200; export type ForgettingAction = 'keep' | 'compress' | 'drop'; export function decideForgetting(input: ForgettingInput): ForgettingAction { const risk = forgettingRisk(input); if (risk >= DROP_THRESHOLD) return 'drop'; if (risk >= COMPRESS_THRESHOLD) return 'compress'; return 'keep'; } - src/mcp/server.ts:808-808 (registration)MCP CallToolRequestSchema switch case wiring: routes 'forget' tool calls to handleForget() function.
case 'forget': text = handleForget(args); break;