6f3e53a56a
Add candidate auto-review pipeline, shadow audit tooling, admin metrics page, and user-visible memory recall hints in chat with phase-a readiness checks. Co-authored-by: Cursor <cursoragent@cursor.com>
97 lines
4.0 KiB
JavaScript
97 lines
4.0 KiB
JavaScript
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,
|
|
},
|
|
};
|
|
}
|