Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143import type { Agent } from "agents";import { OperationFailure } from "./operation-result";import { DEFAULT_INSTRUCTIONS, parseInstructions, type InstructionSettings,} from "../shared/instructions";import { MAX_MEMORIES, MEMORY_SEARCH_LIMIT, memorySearchQuery, parseMemoryContent, validateMemoryId, validateMemoryVersion, type MemoryFact,} from "../shared/memory";
export class PersonalMemory { constructor(private readonly sql: Agent["sql"]) {} initialize() { this.sql`CREATE TABLE IF NOT EXISTS flarebot_instructions ( singleton INTEGER PRIMARY KEY CHECK (singleton = 1), instructions TEXT, updated_at TEXT )`; this .sql`INSERT OR IGNORE INTO flarebot_instructions (singleton) VALUES (1)`;
this.sql`CREATE TABLE IF NOT EXISTS flarebot_memories ( id TEXT PRIMARY KEY, content TEXT, version INTEGER NOT NULL, createdAt TEXT NOT NULL, updatedAt TEXT NOT NULL )`; this.sql`CREATE VIRTUAL TABLE IF NOT EXISTS flarebot_memories_search USING fts5(id UNINDEXED, content, tokenize = 'porter unicode61')`; this .sql`CREATE TRIGGER IF NOT EXISTS flarebot_memory_insert AFTER INSERT ON flarebot_memories WHEN new.content IS NOT NULL BEGIN INSERT INTO flarebot_memories_search(id, content) VALUES (new.id, new.content); END`; this .sql`CREATE TRIGGER IF NOT EXISTS flarebot_memory_update AFTER UPDATE ON flarebot_memories BEGIN DELETE FROM flarebot_memories_search WHERE id = old.id; INSERT INTO flarebot_memories_search(id, content) SELECT new.id, new.content WHERE new.content IS NOT NULL; END`; } getInstructionSettings(): InstructionSettings { const row = this.instructionSettingsRow(); return { instructions: row.instructions ?? DEFAULT_INSTRUCTIONS, defaultInstructions: DEFAULT_INSTRUCTIONS, customized: row.instructions !== null, updatedAt: row.updated_at, }; }
updateInstructions(value: unknown): InstructionSettings { const instructions = parseInstructions(value); this.sql`UPDATE flarebot_instructions SET instructions = ${instructions}, updated_at = ${new Date().toISOString()} WHERE singleton = 1`; return this.getInstructionSettings(); }
resetInstructions(): InstructionSettings { this.sql`UPDATE flarebot_instructions SET instructions = NULL, updated_at = ${new Date().toISOString()} WHERE singleton = 1`; return this.getInstructionSettings(); }
private instructionSettingsRow() { return this.sql<{ instructions: string | null; updated_at: string | null; }>`SELECT instructions, updated_at FROM flarebot_instructions WHERE singleton = 1`[0]; }
readInstructions(): string { return this.instructionSettingsRow().instructions ?? DEFAULT_INSTRUCTIONS; }
listMemories(): MemoryFact[] { return this.sql<MemoryFact>`SELECT * FROM flarebot_memories WHERE content IS NOT NULL ORDER BY createdAt DESC, id LIMIT ${MAX_MEMORIES}`; }
addMemory(value: unknown): MemoryFact { return this.insertMemory(crypto.randomUUID(), value); }
insertMemory(id: string, value: unknown): MemoryFact { const content = parseMemoryContent(value); const existing = this .sql<MemoryFact>`SELECT * FROM flarebot_memories WHERE id = ${id}`[0]; // Keep content-free tombstones: a replay after deletion must never resurrect a fact. if (existing) { if (existing.content === null) throw new OperationFailure("memory_deleted"); return existing; } const [{ count }] = this.sql<{ count: number }>`SELECT count(*) AS count FROM flarebot_memories WHERE content IS NOT NULL`; if (count >= MAX_MEMORIES) throw new OperationFailure("memory_full"); const now = new Date().toISOString(); this .sql`INSERT INTO flarebot_memories VALUES (${id}, ${content}, 1, ${now}, ${now})`; return { id, content, version: 1, createdAt: now, updatedAt: now }; }
updateMemory(id: unknown, value: unknown, version: unknown): MemoryFact { validateMemoryId(id); validateMemoryVersion(version); const content = parseMemoryContent(value); const [updated] = this.sql<MemoryFact>`UPDATE flarebot_memories SET content = ${content}, version = version + 1, updatedAt = ${new Date().toISOString()} WHERE id = ${id} AND content IS NOT NULL AND version = ${version} RETURNING *`; if (!updated) throw new OperationFailure("memory_edit_conflict"); return updated; }
deleteMemory(id: unknown, version: unknown): { deleted: true } { validateMemoryId(id); validateMemoryVersion(version); const [existing] = this .sql<MemoryFact>`SELECT * FROM flarebot_memories WHERE id = ${id}`; if (!existing || existing.content === null) return { deleted: true }; if (existing.version !== version) throw new OperationFailure("memory_delete_conflict"); this.sql`UPDATE flarebot_memories SET content = NULL, version = version + 1, updatedAt = ${new Date().toISOString()} WHERE id = ${id}`; return { deleted: true }; }
searchMemories(value: unknown): MemoryFact[] { const query = memorySearchQuery(value); if (!query) return []; return this.sql<MemoryFact>`SELECT m.* FROM flarebot_memories_search JOIN flarebot_memories m ON m.id = flarebot_memories_search.id WHERE flarebot_memories_search MATCH ${query} AND m.content IS NOT NULL ORDER BY bm25(flarebot_memories_search), m.id LIMIT ${MEMORY_SEARCH_LIMIT}`; }}