import * as path from 'path'; import { toolLogger } from '../../index.js'; import { store, get, set } from './keyv.js'; // Helper function to get storage path const getStoragePath = (options) => { // For now, use default path. Later this can be configured via options return path.join(process.cwd(), 'memory.json'); }; // Default collection name when none provided const DEFAULT_COLLECTION = 'no-collection'; // Helper function to process value based on format const processValueForStorage = (value, format) => { switch (format) { case 'json': try { // Validate JSON by parsing and re-stringifying JSON.parse(value); return value; } catch (error) { throw new Error('Invalid JSON format provided'); } case 'binary': // For binary, we expect base64 encoded data try { // Validate base64 Buffer.from(value, 'base64'); return value; } catch (error) { throw new Error('Invalid base64 format for binary data'); } case 'text': default: return value; } }; // Helper function to create memory entry const createMemoryEntry = (value, format) => { const now = new Date().toISOString(); return { value: processValueForStorage(value, format), meta: { type: format, created: now, updated: now } }; }; export const tools = (target, options) => { const logger = toolLogger('memory', options); const storagePath = getStoragePath(options); return [ { type: 'function', function: { name: 'memorize', description: `Store information in memory as a key-value collection with format support. Supports text, JSON, and binary (base64) formats. Returns: { success: boolean, message: string, meta: { type: "text" | "json" | "binary", created: string (ISO timestamp), updated: string (ISO timestamp) } }`, parameters: { type: 'object', properties: { collection: { type: 'string', description: 'Collection name to organize related data (defaults to "no-collection" if not provided). Acts like a namespace.', optional: true }, key: { type: 'string', description: 'Unique identifier for the data within the collection. Must be a string.' }, value: { type: 'string', description: 'The data to store. For format="text": any string. For format="json": valid JSON string. For format="binary": base64 encoded data.' }, format: { type: 'string', description: 'Data format type. "text" for plain text (default), "json" for JSON data (validates structure), "binary" for base64 encoded binary data.', enum: ['text', 'json', 'binary'], optional: true } }, required: ['key', 'value'] }, function: async (params) => { try { const { collection = DEFAULT_COLLECTION, key, value, format = 'text' } = params; logger.debug(`Tool::Memorize Storing ${key} in collection ${collection} as ${format}`); const memoryEntry = createMemoryEntry(value, format); await set(`${collection}:${key}`, memoryEntry, storagePath, collection); return { success: true, message: `Stored ${key} in collection ${collection} as ${format}`, meta: memoryEntry.meta }; } catch (error) { logger.error('Error storing memory', error); return { success: false, message: error instanceof Error ? error.message : 'Unknown error occurred' }; } }, parse: JSON.parse } }, { type: 'function', function: { name: 'recall', description: `Retrieve stored information from memory by collection and key, including format metadata. Returns: { success: boolean, value?: string (the stored data), meta?: { type: "text" | "json" | "binary", created: string (ISO timestamp), updated: string (ISO timestamp) }, key: string, collection: string, message?: string (error message if success=false) }`, parameters: { type: 'object', properties: { collection: { type: 'string', description: 'Collection name to retrieve from (defaults to "no-collection" if not provided). Must match the collection used when storing.', optional: true }, key: { type: 'string', description: 'The unique identifier of the data to retrieve. Must match the key used when storing.' } }, required: ['key'] }, function: async (params) => { try { const { collection = DEFAULT_COLLECTION, key } = params; logger.debug(`Tool::Recall Retrieving ${key} from collection ${collection}`); const storedData = await get(`${collection}:${key}`, storagePath, collection); if (storedData === undefined) { return { success: false, message: `Key ${key} not found in collection ${collection}` }; } // Handle both old format (plain string) and new format (MemoryEntry) let memoryEntry; if (typeof storedData === 'string') { // Legacy format - convert to new format memoryEntry = { value: storedData, meta: { type: 'text', created: new Date().toISOString(), updated: new Date().toISOString() } }; } else { memoryEntry = storedData; } return { success: true, value: memoryEntry.value, meta: memoryEntry.meta, key, collection }; } catch (error) { logger.error('Error retrieving memory', error); return { success: false, message: error instanceof Error ? error.message : 'Unknown error occurred' }; } }, parse: JSON.parse } }, { type: 'function', function: { name: 'forget', description: `Remove a specific key from memory collection. Returns: { success: boolean, message: string (confirmation or error message) }`, parameters: { type: 'object', properties: { collection: { type: 'string', description: 'Collection name to remove from (defaults to "no-collection" if not provided). Must match the collection where the key was stored.', optional: true }, key: { type: 'string', description: 'The unique identifier of the data to remove. Must match exactly the key used when storing.' } }, required: ['key'] }, function: async (params) => { try { const { collection = DEFAULT_COLLECTION, key } = params; logger.debug(`Tool::Forget Removing ${key} from collection ${collection}`); const keyv = store(storagePath, collection); const deleted = await keyv.delete(`${collection}:${key}`); return { success: deleted, message: deleted ? `Removed ${key} from ${collection}` : `Key ${key} not found in ${collection}` }; } catch (error) { logger.error('Error removing from memory', error); return { success: false, message: error instanceof Error ? error.message : 'Unknown error occurred' }; } }, parse: JSON.parse } }, { type: 'function', function: { name: 'list_memories', description: `List all keys in a specific collection using Keyv's iterator method. Returns: { success: boolean, collection: string (the collection name), keys: string[] (array of key names in the collection), entries: Array<{ key: string, meta?: { type: "text" | "json" | "binary", created: string (ISO timestamp), updated: string (ISO timestamp) } }>, count: number (total number of keys), message?: string (info or error message) }`, parameters: { type: 'object', properties: { collection: { type: 'string', description: 'Collection name to list keys from (defaults to "no-collection" if not provided). Will return all keys stored in this collection namespace.', optional: true } }, required: [] }, function: async (params) => { try { const { collection = DEFAULT_COLLECTION } = params; logger.debug(`Tool::ListMemories Listing keys in collection ${collection}`); // Create a Keyv instance for the specific collection to use iterator const keyv = store(storagePath, collection); const keys = []; const entries = []; try { // Check if iterator method exists and use it if (typeof keyv.iterator === 'function') { try { // Try calling iterator without arguments first const iterator = keyv.iterator(); for await (const [key, value] of iterator) { // Remove the collection prefix from the key to get the clean key name const cleanKey = key.replace(`${collection}:`, ''); keys.push(cleanKey); // Try to extract metadata if it's a MemoryEntry let meta = undefined; if (value && typeof value === 'object' && value.meta) { meta = value.meta; } entries.push({ key: cleanKey, meta }); } } catch (iteratorCallError) { logger.warn(`Tool::ListMemories Iterator call failed:`, iteratorCallError); // Fall through to the not available case return { success: true, collection, keys: [], entries: [], count: 0, message: 'Iterator call failed. Unable to list keys.' }; } } else { // Iterator not available, provide helpful message logger.warn(`Tool::ListMemories Iterator method not available for Keyv instance`); return { success: true, collection, keys: [], entries: [], count: 0, message: 'Iterator method not available in this Keyv version. Use individual key operations instead.' }; } return { success: true, collection, keys, entries, count: keys.length }; } catch (iteratorError) { // If iterator fails, fall back to returning basic info logger.warn(`Tool::ListMemories Iterator failed for collection ${collection}:`, iteratorError); return { success: true, collection, keys: [], entries: [], count: 0, message: 'Iterator failed or collection is empty' }; } } catch (error) { logger.error('Error listing memories', error); return { success: false, message: error instanceof Error ? error.message : 'Unknown error occurred' }; } }, parse: JSON.parse } } ]; }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVtb3J5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpYi90b29scy9tZW1vcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUE7QUFJNUIsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGdCQUFnQixDQUFBO0FBRzNDLE9BQU8sRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxNQUFNLFdBQVcsQ0FBQTtBQUUzQyxzQ0FBc0M7QUFDdEMsTUFBTSxjQUFjLEdBQUcsQ0FBQyxPQUFrQixFQUFVLEVBQUU7SUFDbEQsc0VBQXNFO0lBQ3RFLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEVBQUUsYUFBYSxDQUFDLENBQUM7QUFDbkQsQ0FBQyxDQUFDO0FBRUYsNkNBQTZDO0FBQzdDLE1BQU0sa0JBQWtCLEdBQUcsZUFBZSxDQUFDO0FBZTNDLG1EQUFtRDtBQUNuRCxNQUFNLHNCQUFzQixHQUFHLENBQUMsS0FBYSxFQUFFLE1BQWtCLEVBQVUsRUFBRTtJQUN6RSxRQUFRLE1BQU0sRUFBRTtRQUNaLEtBQUssTUFBTTtZQUNQLElBQUk7Z0JBQ0EsK0NBQStDO2dCQUMvQyxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUNsQixPQUFPLEtBQUssQ0FBQzthQUNoQjtZQUFDLE9BQU8sS0FBSyxFQUFFO2dCQUNaLE1BQU0sSUFBSSxLQUFLLENBQUMsOEJBQThCLENBQUMsQ0FBQzthQUNuRDtRQUNMLEtBQUssUUFBUTtZQUNULDRDQUE0QztZQUM1QyxJQUFJO2dCQUNBLGtCQUFrQjtnQkFDbEIsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsUUFBUSxDQUFDLENBQUM7Z0JBQzdCLE9BQU8sS0FBSyxDQUFDO2FBQ2hCO1lBQUMsT0FBTyxLQUFLLEVBQUU7Z0JBQ1osTUFBTSxJQUFJLEtBQUssQ0FBQyx1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