function normalizeProjectId(value) { const raw = String(value ?? '').trim(); if (!raw) return null; if (!/^[a-zA-Z0-9_-]+$/.test(raw)) { throw new Error(`Invalid MEMORY_MEM0_PROJECT_ID: ${raw}`); } return raw; } function timeoutSignal(timeoutMs) { if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return null; const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), timeoutMs); return { signal: controller.signal, clear() { clearTimeout(timeout); }, }; } function joinUrl(baseUrl, path) { return `${String(baseUrl).replace(/\/$/, '')}/${String(path).replace(/^\//, '')}`; } export function createMem0HttpClient({ apiKey, projectId = null, baseUrl = 'https://api.mem0.ai', writePath = '/v1/memories', compactPath = '/v1/memories/compact', modelProviderKeyId = null, model = null, modelApiType = null, fetchImpl = globalThis.fetch, timeoutMs = 3000, } = {}) { const resolvedApiKey = String(apiKey ?? '').trim(); if (!resolvedApiKey) throw new Error('createMem0HttpClient requires MEMORY_MEM0_API_KEY'); if (typeof fetchImpl !== 'function') throw new Error('createMem0HttpClient requires fetch'); const resolvedProjectId = normalizeProjectId(projectId); async function postJson(path, body) { const timeout = timeoutSignal(Number(timeoutMs)); try { const response = await fetchImpl(joinUrl(baseUrl, path), { method: 'POST', headers: { 'content-type': 'application/json', authorization: `Bearer ${resolvedApiKey}`, }, body: JSON.stringify(body), ...(timeout?.signal ? { signal: timeout.signal } : {}), }); if (!response?.ok) { throw new Error(`Mem0 request failed: ${response?.status ?? 'unknown'}`); } return response.json?.() ?? {}; } finally { timeout?.clear(); } } return { async write(input = {}) { const data = await postJson(writePath, { user_id: input.userId, session_id: input.sessionId, messages: Array.isArray(input.messages) ? input.messages : [], project_id: resolvedProjectId, model_provider_key_id: String(modelProviderKeyId ?? '').trim() || null, model: String(model ?? '').trim() || null, model_api_type: String(modelApiType ?? '').trim() || null, }); return { saved: Number(data?.saved ?? data?.count ?? 1), analyzed: Number(data?.analyzed ?? 1), memories: Number(data?.memories ?? data?.memory_count ?? data?.count ?? 0), }; }, async compact(input = {}) { const data = await postJson(compactPath, { user_id: input.userId, project_id: resolvedProjectId, model_provider_key_id: String(modelProviderKeyId ?? '').trim() || null, model: String(model ?? '').trim() || null, model_api_type: String(modelApiType ?? '').trim() || null, }); return { analyzed: Number(data?.analyzed ?? 1), memories: Number(data?.memories ?? data?.memory_count ?? data?.count ?? 0), }; }, }; } export function createMem0MemoryBackend({ enabled = false, apiKey = null, projectId = null, client = null, unavailableReason = null, } = {}) { const hasApiKey = Boolean(String(apiKey ?? '').trim()); const resolvedProjectId = normalizeProjectId(projectId); const configured = Boolean(enabled && hasApiKey); const hasClient = Boolean(client?.write || client?.compact); const wired = Boolean(configured && hasClient); const reason = unavailableReason ?? (enabled ? (!hasApiKey ? 'api_key_not_configured' : 'client_not_configured') : 'not_configured'); return { name: 'mem0', category: 'extraction', role: 'automatic-memory-generation', flag: 'MEMORY_MEM0_ENABLED', unavailableReason: reason, projectId: resolvedProjectId, isAvailable() { return Boolean(wired); }, getUnavailableReason() { return this.isAvailable() ? null : reason; }, async write(input = {}) { if (!this.isAvailable() || !client?.write) return { saved: 0, analyzed: 0, memories: 0 }; const result = await client.write({ ...input, projectId: resolvedProjectId }); return { saved: Number(result?.saved ?? 0), analyzed: Number(result?.analyzed ?? 0), memories: Number(result?.memories ?? 0), }; }, async compact(input = {}) { if (!this.isAvailable() || !client?.compact) return { analyzed: 0, memories: 0 }; const result = await client.compact({ ...input, projectId: resolvedProjectId }); return { analyzed: Number(result?.analyzed ?? 0), memories: Number(result?.memories ?? 0), }; }, }; }