{"version":3,"file":"agent-memory.mjs","names":[],"sources":["../../../src/services/agent-memory.ts"],"sourcesContent":["/**\n * Agent Memory Service — file-backed declarative memory with frozen snapshots.\n *\n * Inspired by Hermes Agent's memory system. Two stores:\n *   MEMORY.md  — agent's own notes (environment facts, tool quirks, lessons)\n *   USER_{id}.md — per-user profile (preferences, communication style, habits)\n *\n * The \"frozen snapshot\" pattern:\n *   1. At session start, MEMORY.md + USER_{id}.md are read from disk\n *   2. The contents are injected into the system prompt via before_prompt_build\n *   3. Mid-session writes update disk immediately but do NOT change the active\n *      prompt — this preserves the LLM prefix cache\n *   4. Next session sees the updated memory\n *\n * Entries are §-delimited strings inside the markdown files.\n * Character limits prevent unbounded growth.\n *\n * All writes are scanned for prompt injection and credential leaks.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\n\n// ─── Types ───────────────────────────────────────────────────────────────\n\nexport interface MemoryConfig {\n  /** Max characters for agent memory. Default: 2200. */\n  agentCharLimit?: number;\n  /** Max characters per user profile. Default: 1375. */\n  userCharLimit?: number;\n  /** Base directory for memory files. Default: ~/.openclawnch/memory/ */\n  baseDir?: string;\n}\n\nexport interface MemoryEntry {\n  content: string;\n  addedAt: number;\n}\n\ntype StoreType = 'agent' | 'user';\n\n// ─── Injection Detection ─────────────────────────────────────────────────\n\nconst INJECTION_PATTERNS = [\n  /ignore\\s+(all\\s+)?previous\\s+instructions/i,\n  /you\\s+are\\s+now\\s+(a|an|the)\\s+/i,\n  /forget\\s+(everything|all|your)/i,\n  /\\bsystem\\s*:\\s*you\\s+(must|should|will)/i,\n  /<\\/?system[-_]?(message|prompt|instruction)>/i,\n  /\\bdo\\s+not\\s+(tell|reveal|mention|show)\\s+(the\\s+)?user\\b/i,\n  /\\bact\\s+as\\s+if\\s+(you|the)\\s+(are|is|have)\\b/i,\n  /\\b(CLAWNCHER_PRIVATE_KEY|BANKR_API_KEY|PRIVATE_KEY|WALLET_PASSWORD)\\b/i,\n  /\\b(mnemonic|seed\\s*phrase|recovery\\s*phrase|secret\\s*words)\\b/i,\n  /[\\u200B-\\u200F\\u202A-\\u202E\\uFEFF]/,  // invisible unicode\n];\n\nfunction containsInjection(text: string): string | null {\n  for (const pattern of INJECTION_PATTERNS) {\n    if (pattern.test(text)) {\n      return `Blocked: content matches injection pattern \"${pattern.source.slice(0, 40)}\"`;\n    }\n  }\n  return null;\n}\n\n// ─── Entry Delimiter ─────────────────────────────────────────────────────\n\nconst ENTRY_DELIM = '\\n§ ';\nconst ENTRY_DELIM_REGEX = /\\n§ /g;\n\nfunction parseEntries(content: string): string[] {\n  if (!content.trim()) return [];\n  // Split on § delimiter, trim each entry, filter empty\n  return content.split(ENTRY_DELIM_REGEX)\n    .map(e => e.replace(/^§\\s*/, '').trim())\n    .filter(Boolean);\n}\n\nfunction serializeEntries(entries: string[]): string {\n  if (entries.length === 0) return '';\n  return entries.map(e => `§ ${e}`).join('\\n');\n}\n\n// ─── Memory Store ────────────────────────────────────────────────────────\n\nclass MemoryStore {\n  private entries: string[] = [];\n  private readonly filePath: string;\n  private readonly charLimit: number;\n  private readonly storeType: StoreType;\n\n  constructor(filePath: string, charLimit: number, storeType: StoreType) {\n    this.filePath = filePath;\n    this.charLimit = charLimit;\n    this.storeType = storeType;\n    this.load();\n  }\n\n  // ── Read ──\n\n  private load(): void {\n    try {\n      if (existsSync(this.filePath)) {\n        const content = readFileSync(this.filePath, 'utf8');\n        this.entries = parseEntries(content);\n      }\n    } catch {\n      this.entries = [];\n    }\n  }\n\n  getEntries(): string[] {\n    return [...this.entries];\n  }\n\n  getContent(): string {\n    return serializeEntries(this.entries);\n  }\n\n  getTotalChars(): number {\n    return this.getContent().length;\n  }\n\n  // ── Write ──\n\n  add(entry: string): { ok: boolean; error?: string } {\n    const trimmed = entry.trim();\n    if (!trimmed) return { ok: false, error: 'Empty entry' };\n\n    // Security scan\n    const injectionResult = containsInjection(trimmed);\n    if (injectionResult) return { ok: false, error: injectionResult };\n\n    // Check if adding this would exceed the limit\n    const newContent = serializeEntries([...this.entries, trimmed]);\n    if (newContent.length > this.charLimit) {\n      return {\n        ok: false,\n        error: `Entry would exceed ${this.storeType} memory limit (${newContent.length}/${this.charLimit} chars). ` +\n          `Try removing old entries first with the \"remove\" action.`,\n      };\n    }\n\n    // Check for duplicates\n    if (this.entries.some(e => e.toLowerCase() === trimmed.toLowerCase())) {\n      return { ok: false, error: 'Duplicate entry already exists' };\n    }\n\n    this.entries.push(trimmed);\n    this.persist();\n    return { ok: true };\n  }\n\n  replace(oldSubstring: string, newContent: string): { ok: boolean; error?: string } {\n    const trimmedNew = newContent.trim();\n    if (!trimmedNew) return { ok: false, error: 'Replacement content is empty' };\n\n    const injectionResult = containsInjection(trimmedNew);\n    if (injectionResult) return { ok: false, error: injectionResult };\n\n    const idx = this.entries.findIndex(e =>\n      e.toLowerCase().includes(oldSubstring.toLowerCase()),\n    );\n    if (idx === -1) {\n      return { ok: false, error: `No entry containing \"${oldSubstring.slice(0, 50)}\" found` };\n    }\n\n    const updated = [...this.entries];\n    updated[idx] = trimmedNew;\n\n    const newSerialized = serializeEntries(updated);\n    if (newSerialized.length > this.charLimit) {\n      return { ok: false, error: `Replacement would exceed memory limit (${newSerialized.length}/${this.charLimit} chars)` };\n    }\n\n    this.entries = updated;\n    this.persist();\n    return { ok: true };\n  }\n\n  remove(substring: string): { ok: boolean; removed?: string; error?: string } {\n    const idx = this.entries.findIndex(e =>\n      e.toLowerCase().includes(substring.toLowerCase()),\n    );\n    if (idx === -1) {\n      return { ok: false, error: `No entry containing \"${substring.slice(0, 50)}\" found` };\n    }\n\n    const removed = this.entries.splice(idx, 1)[0]!;\n    this.persist();\n    return { ok: true, removed };\n  }\n\n  clear(): void {\n    this.entries = [];\n    this.persist();\n  }\n\n  // ── Persistence ──\n\n  private persist(): void {\n    try {\n      const dir = this.filePath.replace(/\\/[^/]+$/, '');\n      if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n      writeFileSync(this.filePath, this.getContent(), 'utf8');\n    } catch {\n      // Best effort\n    }\n  }\n}\n\n// ─── Agent Memory Service ────────────────────────────────────────────────\n\nclass AgentMemoryService {\n  private config: Required<MemoryConfig>;\n  private agentStore: MemoryStore;\n  private userStores = new Map<string, MemoryStore>();\n  private frozenSnapshots = new Map<string, { content: string; createdAt: number }>(); // sessionKey → snapshot\n  private static readonly MAX_SNAPSHOTS = 50;\n  private static readonly SNAPSHOT_TTL_MS = 4 * 60 * 60 * 1000; // 4 hours\n\n  constructor(config: MemoryConfig = {}) {\n    this.config = {\n      agentCharLimit: config.agentCharLimit ?? 2200,\n      userCharLimit: config.userCharLimit ?? 1375,\n      baseDir: config.baseDir ?? join(\n        process.env.HOME ?? '/tmp', '.openclawnch', 'memory',\n      ),\n    };\n\n    this.agentStore = new MemoryStore(\n      join(this.config.baseDir, 'MEMORY.md'),\n      this.config.agentCharLimit,\n      'agent',\n    );\n  }\n\n  // ── Frozen Snapshots ───────────────────────────────────────────────\n\n  /**\n   * Take a frozen snapshot for a session.\n   * Call this at session start (before_prompt_build). The snapshot is\n   * immutable for the duration of the session.\n   */\n  freezeSnapshot(sessionKey: string, userId?: string): string {\n    const parts: string[] = [];\n\n    const agentContent = this.agentStore.getContent();\n    if (agentContent) {\n      parts.push('## Agent Memory\\n' + agentContent);\n    }\n\n    if (userId) {\n      const userStore = this.getUserStore(userId);\n      const userContent = userStore.getContent();\n      if (userContent) {\n        parts.push('## User Profile\\n' + userContent);\n      }\n    }\n\n    const snapshot = parts.join('\\n\\n');\n    this.evictStaleSnapshots();\n    this.frozenSnapshots.set(sessionKey, { content: snapshot, createdAt: Date.now() });\n    return snapshot;\n  }\n\n  /**\n   * Get the frozen snapshot for a session (returns empty string if none/expired).\n   */\n  getSnapshot(sessionKey: string): string {\n    const entry = this.frozenSnapshots.get(sessionKey);\n    if (!entry) return '';\n    // Auto-expire stale snapshots on read\n    if (Date.now() - entry.createdAt > AgentMemoryService.SNAPSHOT_TTL_MS) {\n      this.frozenSnapshots.delete(sessionKey);\n      return '';\n    }\n    return entry.content;\n  }\n\n  /**\n   * Clear the frozen snapshot when a session ends.\n   */\n  clearSnapshot(sessionKey: string): void {\n    this.frozenSnapshots.delete(sessionKey);\n  }\n\n  /**\n   * Evict stale/oversized snapshots to prevent unbounded memory growth.\n   */\n  private evictStaleSnapshots(): void {\n    const now = Date.now();\n    // Remove expired entries\n    for (const [key, entry] of this.frozenSnapshots) {\n      if (now - entry.createdAt > AgentMemoryService.SNAPSHOT_TTL_MS) {\n        this.frozenSnapshots.delete(key);\n      }\n    }\n    // If still over limit, remove oldest entries\n    while (this.frozenSnapshots.size >= AgentMemoryService.MAX_SNAPSHOTS) {\n      const oldestKey = this.frozenSnapshots.keys().next().value;\n      if (oldestKey !== undefined) {\n        this.frozenSnapshots.delete(oldestKey);\n      } else {\n        break;\n      }\n    }\n  }\n\n  // ── Agent Memory ───────────────────────────────────────────────────\n\n  addAgentMemory(entry: string): { ok: boolean; error?: string } {\n    return this.agentStore.add(entry);\n  }\n\n  replaceAgentMemory(oldSubstring: string, newContent: string): { ok: boolean; error?: string } {\n    return this.agentStore.replace(oldSubstring, newContent);\n  }\n\n  removeAgentMemory(substring: string): { ok: boolean; removed?: string; error?: string } {\n    return this.agentStore.remove(substring);\n  }\n\n  getAgentMemory(): string[] {\n    return this.agentStore.getEntries();\n  }\n\n  getAgentMemoryStats(): { entries: number; chars: number; limit: number } {\n    return {\n      entries: this.agentStore.getEntries().length,\n      chars: this.agentStore.getTotalChars(),\n      limit: this.config.agentCharLimit,\n    };\n  }\n\n  // ── User Memory ────────────────────────────────────────────────────\n\n  private getUserStore(userId: string): MemoryStore {\n    // Sanitize userId for filesystem safety\n    const safeId = userId.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 64);\n\n    let store = this.userStores.get(safeId);\n    if (!store) {\n      store = new MemoryStore(\n        join(this.config.baseDir, `USER_${safeId}.md`),\n        this.config.userCharLimit,\n        'user',\n      );\n      this.userStores.set(safeId, store);\n    }\n    return store;\n  }\n\n  addUserMemory(userId: string, entry: string): { ok: boolean; error?: string } {\n    return this.getUserStore(userId).add(entry);\n  }\n\n  replaceUserMemory(userId: string, oldSubstring: string, newContent: string): { ok: boolean; error?: string } {\n    return this.getUserStore(userId).replace(oldSubstring, newContent);\n  }\n\n  removeUserMemory(userId: string, substring: string): { ok: boolean; removed?: string; error?: string } {\n    return this.getUserStore(userId).remove(substring);\n  }\n\n  getUserMemory(userId: string): string[] {\n    return this.getUserStore(userId).getEntries();\n  }\n\n  getUserMemoryStats(userId: string): { entries: number; chars: number; limit: number } {\n    const store = this.getUserStore(userId);\n    return {\n      entries: store.getEntries().length,\n      chars: store.getTotalChars(),\n      limit: this.config.userCharLimit,\n    };\n  }\n\n  // ── Diagnostics ────────────────────────────────────────────────────\n\n  getStatus(): {\n    agentEntries: number;\n    agentChars: number;\n    agentCharLimit: number;\n    userStoreCount: number;\n    frozenSnapshotCount: number;\n  } {\n    return {\n      agentEntries: this.agentStore.getEntries().length,\n      agentChars: this.agentStore.getTotalChars(),\n      agentCharLimit: this.config.agentCharLimit,\n      userStoreCount: this.userStores.size,\n      frozenSnapshotCount: this.frozenSnapshots.size,\n    };\n  }\n}\n\n// ─── Singleton ───────────────────────────────────────────────────────────\n\nlet _instance: AgentMemoryService | null = null;\n\nexport function getAgentMemory(config?: MemoryConfig): AgentMemoryService {\n  if (!_instance) {\n    _instance = new AgentMemoryService(config);\n  }\n  return _instance;\n}\n\nexport function resetAgentMemory(): void {\n  _instance = null;\n}\n"],"mappings":"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"}