const VALID_CATEGORIES = new Set(['semantic', 'extraction', 'lifecycle', 'behavior', 'policy']); const VALID_CAPABILITIES = new Set(['resolve', 'write', 'compact']); function kebabCase(value) { return String(value ?? '') .trim() .replace(/([a-z0-9])([A-Z])/g, '$1-$2') .replace(/[^a-zA-Z0-9]+/g, '-') .replace(/^-+|-+$/g, '') .toLowerCase(); } function screamingSnake(value) { return String(value ?? '') .trim() .replace(/([a-z0-9])([A-Z])/g, '$1_$2') .replace(/[^a-zA-Z0-9]+/g, '_') .replace(/^_+|_+$/g, '') .toUpperCase(); } function normalizeCapabilities(capabilities) { const normalized = [...new Set( (Array.isArray(capabilities) ? capabilities : String(capabilities ?? '').split(',')) .map((item) => String(item).trim()) .filter(Boolean), )]; if (normalized.length === 0) throw new Error('At least one capability is required'); for (const capability of normalized) { if (!VALID_CAPABILITIES.has(capability)) { throw new Error(`Unsupported Memory V2 capability: ${capability}`); } } return normalized; } export function normalizeMemoryV2AdapterScaffoldOptions({ name, category, role = 'optional-plugin', capabilities, flag = null, } = {}) { const normalizedName = kebabCase(name); if (!normalizedName) throw new Error('Adapter name is required'); const normalizedCategory = String(category ?? '').trim(); if (!VALID_CATEGORIES.has(normalizedCategory)) { throw new Error(`Adapter category must be one of: ${[...VALID_CATEGORIES].join(', ')}`); } const normalizedCapabilities = normalizeCapabilities(capabilities); const normalizedFlag = flag ? screamingSnake(flag) : `MEMORY_${screamingSnake(normalizedName)}_ENABLED`; return { name: normalizedName, factoryName: `create${normalizedName .split('-') .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join('')}MemoryBackend`, category: normalizedCategory, role: kebabCase(role) || 'optional-plugin', capabilities: normalizedCapabilities, flag: normalizedFlag, }; } function operationMethod(operation) { if (operation === 'resolve') { return `\n async resolve() {\n return {\n memories: [],\n semanticMemories: [],\n behaviorSummary: null,\n activeGoals: [],\n };\n },`; } if (operation === 'write') { return `\n async write() {\n return { saved: 0, analyzed: 0, memories: 0 };\n },`; } if (operation === 'compact') { return `\n async compact() {\n return { analyzed: 0, memories: 0 };\n },`; } return ''; } export function renderMemoryV2AdapterScaffold(options = {}) { const normalized = normalizeMemoryV2AdapterScaffoldOptions(options); const methods = normalized.capabilities.map((capability) => operationMethod(capability)).join('\n'); return `// Generated Memory V2 backend scaffold.\n// Keep disabled by default. Wire real clients only through memory-v2-runtime.mjs.\n\nexport function ${normalized.factoryName}({ enabled = false, unavailableReason = 'not_configured' } = {}) {\n return {\n name: '${normalized.name}',\n category: '${normalized.category}',\n role: '${normalized.role}',\n flag: '${normalized.flag}',\n unavailableReason,\n\n isAvailable() {\n return Boolean(enabled);\n },\n\n getUnavailableReason() {\n return this.isAvailable() ? null : unavailableReason;\n },${methods}\n };\n}\n`; }