const REQUIRED_STATUS_KEYS = [ 'enabled', 'backend', 'selectedBackend', 'profileEnabled', 'eventLogEnabled', 'vectorEnabled', 'failOpen', 'backends', ]; function pass(name, details = {}) { return { name, ok: true, ...details }; } function fail(name, message, details = {}) { return { name, ok: false, message, ...details }; } function findBackend(status, name) { return Array.isArray(status?.backends) ? status.backends.find((backend) => backend?.name === name) : null; } function missingStatusKeys(status) { return REQUIRED_STATUS_KEYS.filter((key) => !(key in Object(status))); } export function evaluateMemoryV2Health({ status, writeResult = null, compactResult = null, expectedBackend = null, requireEnabled = false, requireFailOpen = true, } = {}) { const checks = []; const missing = missingStatusKeys(status); checks.push( missing.length === 0 ? pass('status_contract') : fail('status_contract', `Missing status keys: ${missing.join(', ')}`, { missing }), ); if (requireEnabled) { checks.push( status?.enabled === true ? pass('memory_enabled') : fail('memory_enabled', 'MEMORY_ENABLED is not enabled', { enabled: status?.enabled }), ); } else { checks.push(pass('memory_enabled_optional', { enabled: Boolean(status?.enabled) })); } if (requireFailOpen) { checks.push( status?.failOpen === true ? pass('fail_open') : fail('fail_open', 'MEMORY_FAIL_OPEN must remain enabled for release safety', { failOpen: status?.failOpen, }), ); } if (expectedBackend) { checks.push( status?.backend === expectedBackend ? pass('expected_backend', { backend: status?.backend }) : fail('expected_backend', `Expected MEMORY_BACKEND=${expectedBackend}`, { backend: status?.backend, }), ); } const legacy = findBackend(status, 'legacy-conversation-memory'); checks.push( legacy?.available === true ? pass('legacy_available') : fail('legacy_available', 'Legacy conversation-memory backend must be available', { legacy, }), ); checks.push( legacy?.supports?.write === true && legacy?.supports?.compact === true ? pass('legacy_write_compact_supported') : fail('legacy_write_compact_supported', 'Legacy backend must support write and compact', { supports: legacy?.supports ?? null, }), ); const selectedBackend = status?.selectedBackend ?? null; checks.push( selectedBackend ? pass('selected_backend_present', { selectedBackend }) : fail('selected_backend_present', 'No selected Memory V2 backend for resolve'), ); const selected = findBackend(status, selectedBackend); checks.push( !selected || selected.available === true ? pass('selected_backend_available', { selectedBackend }) : fail('selected_backend_available', 'Selected backend is not available', { selected }), ); const pgvector = findBackend(status, 'pgvector'); if (status?.backend === 'pgvector') { checks.push( pgvector?.available === true ? pass('pgvector_canary_available') : pass('pgvector_canary_fallback', { reason: pgvector?.reason ?? 'unavailable', selectedBackend, }), ); } if (writeResult) { checks.push( writeResult.source === 'legacy-conversation-memory' ? pass('write_uses_legacy', { source: writeResult.source }) : fail('write_uses_legacy', 'Memory V2 write must use legacy backend during semantic canary', { source: writeResult.source ?? null, }), ); } if (compactResult) { checks.push( compactResult.source === 'legacy-conversation-memory' ? pass('compact_uses_legacy', { source: compactResult.source }) : fail('compact_uses_legacy', 'Memory V2 compact must use legacy backend during semantic canary', { source: compactResult.source ?? null, }), ); } return { ok: checks.every((check) => check.ok), checkedAt: new Date().toISOString(), summary: { enabled: Boolean(status?.enabled), backend: status?.backend ?? null, selectedBackend, failOpen: Boolean(status?.failOpen), backendCount: Array.isArray(status?.backends) ? status.backends.length : 0, }, checks, }; }