import { tool } from '@strands-agents/sdk' import { z } from 'zod' import { get, set, del, keys } from 'idb-keyval' const PREFIX = 'careless-v2-memory:' /** remember — store arbitrary structured memory with tags */ export const rememberTool = tool({ name: 'remember', description: 'Store a memory for future retrieval. Memories persist across sessions in IndexedDB.', inputSchema: z.object({ key: z.string().describe('Unique identifier for this memory'), content: z.string().describe('What to remember'), tags: z.array(z.string()).optional().describe('Tags for categorization'), }), callback: async (input) => { try { const entry = { key: input.key, content: input.content, tags: input.tags || [], created: Date.now(), updated: Date.now(), } await set(PREFIX + input.key, entry) return JSON.stringify({ status: 'success', key: input.key }) } catch (err: unknown) { return JSON.stringify({ status: 'error', error: (err as Error).message }) } }, }) export const recallTool = tool({ name: 'recall', description: 'Retrieve a memory by key', inputSchema: z.object({ key: z.string() }), callback: async (input) => { try { const entry = await get(PREFIX + input.key) if (!entry) return JSON.stringify({ status: 'not_found', key: input.key }) return JSON.stringify({ status: 'success', entry }) } catch (err: unknown) { return JSON.stringify({ status: 'error', error: (err as Error).message }) } }, }) export const forgetTool = tool({ name: 'forget', description: 'Delete a memory by key', inputSchema: z.object({ key: z.string() }), callback: async (input) => { try { await del(PREFIX + input.key); return JSON.stringify({ status: 'success' }) } catch (err: unknown) { return JSON.stringify({ status: 'error', error: (err as Error).message }) } }, }) export const listMemoriesTool = tool({ name: 'list_memories', description: 'List all stored memories (optionally filtered by tag)', inputSchema: z.object({ tag: z.string().optional() }), callback: async (input) => { try { const allKeys = await keys() const memoryKeys = allKeys.filter(k => typeof k === 'string' && k.startsWith(PREFIX)) as string[] const entries = await Promise.all(memoryKeys.map(async k => ({ k, v: await get(k) }))) let results = entries.filter(e => e.v).map(e => e.v) if (input.tag) results = results.filter((e: { tags?: string[] }) => e.tags?.includes(input.tag!)) return JSON.stringify({ status: 'success', count: results.length, memories: results }) } catch (err: unknown) { return JSON.stringify({ status: 'error', error: (err as Error).message }) } }, }) export const searchMemoriesTool = tool({ name: 'search_memories', description: 'Full-text search memories by content', inputSchema: z.object({ query: z.string() }), callback: async (input) => { try { const allKeys = await keys() const memoryKeys = allKeys.filter(k => typeof k === 'string' && k.startsWith(PREFIX)) as string[] const entries = await Promise.all(memoryKeys.map(async k => await get(k))) const q = input.query.toLowerCase() const matches = entries .filter(e => e && (e.content?.toLowerCase().includes(q) || e.key?.toLowerCase().includes(q) || e.tags?.some((t: string) => t.toLowerCase().includes(q)))) .sort((a: { updated: number }, b: { updated: number }) => b.updated - a.updated) return JSON.stringify({ status: 'success', count: matches.length, results: matches.slice(0, 20) }) } catch (err: unknown) { return JSON.stringify({ status: 'error', error: (err as Error).message }) } }, }) export const MEMORY_TOOLS = [rememberTool, recallTool, forgetTool, listMemoriesTool, searchMemoriesTool]