const TRUE_VALUES = new Set(['1', 'true', 'yes', 'on']); function readFlag(env, key, fallback = false) { const raw = env?.[key]; if (raw == null || raw === '') return fallback; return TRUE_VALUES.has(String(raw).trim().toLowerCase()); } function normalizeUserIds(value) { return String(value ?? '') .split(/[\s,]+/u) .map((item) => item.trim()) .filter(Boolean); } export function evaluateMemoryV2PhaseAReadiness(env = process.env) { const issues = []; const warnings = []; const agentCanaryUserIds = normalizeUserIds(env.MEMORY_AGENT_CANARY_USER_IDS); const lifecycleUserIds = normalizeUserIds(env.MEMORY_LIFECYCLE_ROLLOUT_USER_IDS); const candidateMode = String(env.MEMORY_CANDIDATE_MODE ?? 'off').trim().toLowerCase(); const injectionMode = String(env.MEMORY_AGENT_INJECTION_MODE ?? 'off').trim().toLowerCase(); const lifecycleMode = String(env.MEMORY_LIFECYCLE_ROLLOUT_MODE ?? 'off').trim().toLowerCase(); if (!readFlag(env, 'MEMORY_ENABLED')) { issues.push({ code: 'memory_disabled', message: 'MEMORY_ENABLED must be 1' }); } if (!readFlag(env, 'MEMORY_CANDIDATE_ENABLED')) { issues.push({ code: 'candidate_disabled', message: 'MEMORY_CANDIDATE_ENABLED must be 1' }); } if (candidateMode !== 'canary') { issues.push({ code: 'candidate_mode', message: `MEMORY_CANDIDATE_MODE must be canary (current: ${candidateMode || 'off'})` }); } if (readFlag(env, 'MEMORY_CANDIDATE_AUTO_ACCEPT_ALL')) { issues.push({ code: 'auto_accept_all', message: 'MEMORY_CANDIDATE_AUTO_ACCEPT_ALL must stay 0 in Phase A' }); } if (!readFlag(env, 'MEMORY_CANDIDATE_PERSISTENCE_ENABLED')) { issues.push({ code: 'candidate_persistence', message: 'MEMORY_CANDIDATE_PERSISTENCE_ENABLED must be 1' }); } if (!readFlag(env, 'MEMORY_AGENT_RESOLVE_ENABLED')) { issues.push({ code: 'agent_resolve_disabled', message: 'MEMORY_AGENT_RESOLVE_ENABLED must be 1' }); } if (injectionMode !== 'canary') { issues.push({ code: 'injection_mode', message: `MEMORY_AGENT_INJECTION_MODE must be canary (current: ${injectionMode || 'off'})` }); } if (agentCanaryUserIds.length === 0) { issues.push({ code: 'agent_canary_users_missing', message: 'MEMORY_AGENT_CANARY_USER_IDS must list at least one user id' }); } if (!readFlag(env, 'MEMORY_LIFECYCLE_ENABLED')) { issues.push({ code: 'lifecycle_disabled', message: 'MEMORY_LIFECYCLE_ENABLED must be 1' }); } if (lifecycleMode !== 'canary') { issues.push({ code: 'lifecycle_mode', message: `MEMORY_LIFECYCLE_ROLLOUT_MODE must be canary (current: ${lifecycleMode || 'off'})` }); } if (lifecycleUserIds.length === 0) { issues.push({ code: 'lifecycle_users_missing', message: 'MEMORY_LIFECYCLE_ROLLOUT_USER_IDS must list at least one user id' }); } if (agentCanaryUserIds.join(',') !== lifecycleUserIds.join(',')) { issues.push({ code: 'canary_user_mismatch', message: 'MEMORY_AGENT_CANARY_USER_IDS and MEMORY_LIFECYCLE_ROLLOUT_USER_IDS must match exactly', }); } if (!readFlag(env, 'MEMORY_PROMOTION_ENABLED')) { issues.push({ code: 'promotion_disabled', message: 'MEMORY_PROMOTION_ENABLED must be 1' }); } if (readFlag(env, 'MEMORY_LIFECYCLE_FORGETTING_ENABLED')) { issues.push({ code: 'forgetting_enabled', message: 'MEMORY_LIFECYCLE_FORGETTING_ENABLED must stay 0 in Phase A' }); } if (readFlag(env, 'MEMORY_LIFECYCLE_DECAY_ENABLED')) { issues.push({ code: 'decay_enabled', message: 'MEMORY_LIFECYCLE_DECAY_ENABLED must stay 0 in Phase A' }); } const pgConfigured = Boolean(String(env.MEMORY_PGVECTOR_DATABASE_URL ?? '').trim()) && Boolean(String(env.MEMORY_PGVECTOR_EMBEDDING_MODULE ?? '').trim()); if (!pgConfigured) { warnings.push({ code: 'pgvector_not_configured', message: 'pgvector URL/embedding module not configured; recall will stay legacy-only until configured', }); } return { ok: issues.length === 0, issues, warnings, summary: { candidateMode, injectionMode, lifecycleMode, agentCanaryUserIds, lifecycleUserIds, pgConfigured, }, }; }