UNPKG

quantum-cli-core

Version:

Quantum CLI Core - Multi-LLM Collaboration System

176 lines (170 loc) 7.99 kB
/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import { BaseTool } from './tools.js'; import * as fs from 'fs/promises'; import * as path from 'path'; import { homedir } from 'os'; const memoryToolSchemaData = { name: 'save_memory', description: 'Saves a specific piece of information or fact to your long-term memory. Use this when the user explicitly asks you to remember something, or when they state a clear, concise fact that seems important to retain for future interactions.', parameters: { type: 'object', properties: { fact: { type: 'string', description: 'The specific fact or piece of information to remember. Should be a clear, self-contained statement.', }, }, required: ['fact'], }, }; const memoryToolDescription = ` Saves a specific piece of information or fact to your long-term memory. Use this tool: - When the user explicitly asks you to remember something (e.g., "Remember that I like pineapple on pizza", "Please save this: my cat's name is Whiskers"). - When the user states a clear, concise fact about themselves, their preferences, or their environment that seems important for you to retain for future interactions to provide a more personalized and effective assistance. Do NOT use this tool: - To remember conversational context that is only relevant for the current session. - To save long, complex, or rambling pieces of text. The fact should be relatively short and to the point. - If you are unsure whether the information is a fact worth remembering long-term. If in doubt, you can ask the user, "Should I remember that for you?" ## Parameters - \`fact\` (string, required): The specific fact or piece of information to remember. This should be a clear, self-contained statement. For example, if the user says "My favorite color is blue", the fact would be "My favorite color is blue". `; export const GEMINI_CONFIG_DIR = '.quantum'; export const QUANTUM_CONFIG_DIR = GEMINI_CONFIG_DIR; // Alias for compatibility export const DEFAULT_CONTEXT_FILENAME = 'QUANTUM.md'; export const MEMORY_SECTION_HEADER = '## Gemini Added Memories'; // This variable will hold the currently configured filename for GEMINI.md context files. // It defaults to DEFAULT_CONTEXT_FILENAME but can be overridden by setGeminiMdFilename. let currentGeminiMdFilename = DEFAULT_CONTEXT_FILENAME; export function setGeminiMdFilename(newFilename) { if (Array.isArray(newFilename)) { if (newFilename.length > 0) { currentGeminiMdFilename = newFilename.map((name) => name.trim()); } } else if (newFilename && newFilename.trim() !== '') { currentGeminiMdFilename = newFilename.trim(); } } // Alias for compatibility export const setQuantumMdFilename = setGeminiMdFilename; export function getCurrentGeminiMdFilename() { if (Array.isArray(currentGeminiMdFilename)) { return currentGeminiMdFilename[0]; } return currentGeminiMdFilename; } export function getAllGeminiMdFilenames() { if (Array.isArray(currentGeminiMdFilename)) { return currentGeminiMdFilename; } return [currentGeminiMdFilename]; } // Alias for compatibility export const getAllQuantumMdFilenames = getAllGeminiMdFilenames; function getGlobalMemoryFilePath() { return path.join(homedir(), GEMINI_CONFIG_DIR, getCurrentGeminiMdFilename()); } /** * Ensures proper newline separation before appending content. */ function ensureNewlineSeparation(currentContent) { if (currentContent.length === 0) return ''; if (currentContent.endsWith('\n\n') || currentContent.endsWith('\r\n\r\n')) return ''; if (currentContent.endsWith('\n') || currentContent.endsWith('\r\n')) return '\n'; return '\n\n'; } export class MemoryTool extends BaseTool { static Name = memoryToolSchemaData.name; constructor() { super(MemoryTool.Name, 'Save Memory', memoryToolDescription, memoryToolSchemaData.parameters); } static async performAddMemoryEntry(text, memoryFilePath, fsAdapter) { let processedText = text.trim(); // Remove leading hyphens and spaces that might be misinterpreted as markdown list items processedText = processedText.replace(/^(-+\s*)+/, '').trim(); const newMemoryItem = `- ${processedText}`; try { await fsAdapter.mkdir(path.dirname(memoryFilePath), { recursive: true }); let content = ''; try { content = await fsAdapter.readFile(memoryFilePath, 'utf-8'); } catch (_e) { // File doesn't exist, will be created with header and item. } const headerIndex = content.indexOf(MEMORY_SECTION_HEADER); if (headerIndex === -1) { // Header not found, append header and then the entry const separator = ensureNewlineSeparation(content); content += `${separator}${MEMORY_SECTION_HEADER}\n${newMemoryItem}\n`; } else { // Header found, find where to insert the new memory entry const startOfSectionContent = headerIndex + MEMORY_SECTION_HEADER.length; let endOfSectionIndex = content.indexOf('\n## ', startOfSectionContent); if (endOfSectionIndex === -1) { endOfSectionIndex = content.length; // End of file } const beforeSectionMarker = content .substring(0, startOfSectionContent) .trimEnd(); let sectionContent = content .substring(startOfSectionContent, endOfSectionIndex) .trimEnd(); const afterSectionMarker = content.substring(endOfSectionIndex); sectionContent += `\n${newMemoryItem}`; content = `${beforeSectionMarker}\n${sectionContent.trimStart()}\n${afterSectionMarker}`.trimEnd() + '\n'; } await fsAdapter.writeFile(memoryFilePath, content, 'utf-8'); } catch (error) { console.error(`[MemoryTool] Error adding memory entry to ${memoryFilePath}:`, error); throw new Error(`[MemoryTool] Failed to add memory entry: ${error instanceof Error ? error.message : String(error)}`); } } async execute(params, _signal) { const { fact } = params; if (!fact || typeof fact !== 'string' || fact.trim() === '') { const errorMessage = 'Parameter "fact" must be a non-empty string.'; return { llmContent: JSON.stringify({ success: false, error: errorMessage }), returnDisplay: `Error: ${errorMessage}`, }; } try { // Use the static method with actual fs promises await MemoryTool.performAddMemoryEntry(fact, getGlobalMemoryFilePath(), { readFile: fs.readFile, writeFile: fs.writeFile, mkdir: fs.mkdir, }); const successMessage = `Okay, I've remembered that: "${fact}"`; return { llmContent: JSON.stringify({ success: true, message: successMessage }), returnDisplay: successMessage, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`[MemoryTool] Error executing save_memory for fact "${fact}": ${errorMessage}`); return { llmContent: JSON.stringify({ success: false, error: `Failed to save memory. Detail: ${errorMessage}`, }), returnDisplay: `Error saving memory: ${errorMessage}`, }; } } } //# sourceMappingURL=memoryTool.js.map