{"version":3,"sources":["../../../../src/server/stores/memory-mem0/index.ts","../../../../src/server/stores/memory-mem0/adapter.ts","../../../../src/server/http.ts"],"sourcesContent":["/**\n * mem0 memory adapter — public entry.\n *\n *   import { createMem0Memory } from '@mordn/chat-widget/server/memory/mem0';\n *   createChatHandler({\n *     memory: { adapter: createMem0Memory({ apiKey: process.env.MEM0_API_KEY, agentId: 'support-bot' }) },\n *   });\n *\n * Same handler, same /memory routes, same security as the Drizzle default — only\n * the backend changes. mem0 extracts + consolidates server-side, so no\n * extractionModel is needed.\n */\nimport 'server-only';\n\nexport { createMem0Memory, type Mem0Options } from './adapter';\n","/**\n * mem0 memory adapter — a thin `MemoryAdapter` over the mem0 REST API.\n *\n * mem0 already does per-user memory: extraction + consolidation happen\n * SERVER-SIDE, so `record` just passes the raw turn and mem0 extracts. The\n * mapping is almost 1:1, which validates the contract:\n *\n *   retrieve({query,limit}) → POST /v1/memories/search  (filters: user_id + agent_id)\n *   record({messages})      → POST /v1/memories         (mem0 extracts)\n *   list()                  → GET  /v1/memories         (user_id + agent_id)\n *   forget(id)              → DELETE /v1/memories/:id\n *   forgetAll()             → DELETE /v1/memories        (user_id + agent_id)\n *\n * The bound `userId` becomes mem0's `user_id`; the per-request factory is the\n * IDOR boundary; the agent namespace becomes mem0's `agent_id`. No extraction\n * model of our own — mem0 is the extractor. The API key is server-side only\n * (`server-only`), like the Supabase service-role key.\n *\n * NOTE: mem0's exact field names/paths vary by version; this client targets the\n * v1 REST shape and is intentionally tolerant when parsing responses. Adjust\n * `baseUrl`/paths for self-hosted mem0 (OSS) vs. the managed platform.\n */\n\nimport 'server-only';\nimport type {\n  Memory,\n  MemoryAdapter,\n  MemoryAdapterFactory,\n  RecordOptions,\n  RetrieveOptions,\n} from '../../memory/types';\nimport { withFetchTimeout, DEFAULT_HTTP_TIMEOUT_MS } from '../../http';\n\nconst DEFAULT_BASE_URL = 'https://api.mem0.ai';\n\nfunction normaliseMem0(raw: Record<string, unknown>): Memory {\n  return {\n    id: String(raw.id ?? raw.memory_id ?? ''),\n    text: String(raw.memory ?? raw.text ?? raw.data ?? ''),\n    score: raw.score != null ? Number(raw.score) : undefined,\n    createdAt: String(raw.created_at ?? raw.createdAt ?? new Date().toISOString()),\n    metadata: (raw.metadata as Record<string, unknown>) ?? {},\n  };\n}\n\nclass Mem0MemoryAdapter implements MemoryAdapter {\n  private readonly base: string;\n  private readonly doFetch: typeof fetch;\n\n  constructor(\n    public readonly userId: string,\n    private readonly apiKey: string,\n    private readonly agentId: string,\n    baseUrl: string,\n    fetchImpl: typeof fetch,\n  ) {\n    this.base = baseUrl.replace(/\\/$/, '');\n    this.doFetch = fetchImpl;\n  }\n\n  private headers(): Record<string, string> {\n    return { Authorization: `Token ${this.apiKey}`, 'Content-Type': 'application/json' };\n  }\n\n  private filters() {\n    return { user_id: this.userId, agent_id: this.agentId };\n  }\n\n  async retrieve(opts: RetrieveOptions): Promise<Memory[]> {\n    try {\n      const res = await this.doFetch(`${this.base}/v1/memories/search/`, {\n        method: 'POST',\n        headers: this.headers(),\n        body: JSON.stringify({ query: opts.query, limit: opts.limit ?? 8, ...this.filters() }),\n      });\n      if (!res.ok) return [];\n      const data = (await res.json().catch(() => null)) as unknown;\n      const rows = Array.isArray(data) ? data : ((data as { results?: unknown[] })?.results ?? []);\n      return (rows as Record<string, unknown>[])\n        .map(normaliseMem0)\n        .filter((m) => (opts.minScore != null && m.score != null ? m.score >= opts.minScore : true));\n    } catch {\n      return [];\n    }\n  }\n\n  async record(opts: RecordOptions): Promise<void> {\n    // mem0 accepts the raw messages and extracts server-side.\n    await this.doFetch(`${this.base}/v1/memories/`, {\n      method: 'POST',\n      headers: this.headers(),\n      body: JSON.stringify({\n        messages: opts.messages,\n        ...this.filters(),\n        metadata: { conversationId: opts.conversationId },\n      }),\n    }).catch(() => {});\n  }\n\n  async list(): Promise<Memory[]> {\n    const params = new URLSearchParams({ user_id: this.userId, agent_id: this.agentId });\n    const res = await this.doFetch(`${this.base}/v1/memories/?${params}`, { headers: this.headers() });\n    if (!res.ok) return [];\n    const data = (await res.json().catch(() => null)) as unknown;\n    const rows = Array.isArray(data) ? data : ((data as { results?: unknown[] })?.results ?? []);\n    return (rows as Record<string, unknown>[]).map(normaliseMem0);\n  }\n\n  async forget(id: string): Promise<void> {\n    await this.doFetch(`${this.base}/v1/memories/${encodeURIComponent(id)}/`, {\n      method: 'DELETE',\n      headers: this.headers(),\n    }).catch(() => {});\n  }\n\n  async forgetAll(): Promise<void> {\n    const params = new URLSearchParams({ user_id: this.userId, agent_id: this.agentId });\n    await this.doFetch(`${this.base}/v1/memories/?${params}`, {\n      method: 'DELETE',\n      headers: this.headers(),\n    }).catch(() => {});\n  }\n}\n\nexport interface Mem0Options {\n  /** mem0 API key. Required. Server-side only. */\n  apiKey: string;\n  /** Agent namespace → mem0's `agent_id`. Default 'default'. */\n  agentId?: string;\n  /** API base URL. Defaults to the managed platform; override for self-host. */\n  baseUrl?: string;\n  /** Optional fetch override (testing). */\n  fetch?: typeof fetch;\n  /** Per-request timeout (ms) for the mem0 API. Defaults to 30s; `0` disables. */\n  timeoutMs?: number;\n}\n\n/**\n * Create a `MemoryAdapterFactory` backed by mem0. Because mem0 extracts\n * server-side, drop `extractionModel` — there's nothing to configure locally.\n *\n *   adapter: createMem0Memory({ apiKey: process.env.MEM0_API_KEY, agentId: 'support-bot' })\n */\nexport function createMem0Memory(options: Mem0Options): MemoryAdapterFactory {\n  if (!options.apiKey) throw new Error('[chat-widget] createMem0Memory requires an apiKey');\n  const agentId = options.agentId ?? 'default';\n  const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n  const fetchImpl = withFetchTimeout(options.fetch ?? fetch, options.timeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS);\n  return (userId) => new Mem0MemoryAdapter(userId, options.apiKey, agentId, baseUrl, fetchImpl);\n}\n","/**\n * Shared HTTP helper for the hosted API clients.\n *\n * Every hosted client (chat store, storage, config, feedback, knowledge, memory,\n * mem0) issues `fetch` to a remote service. Without a timeout, a hung or stalled\n * upstream holds the request — and the connection-pool slot / serverless compute\n * behind it — open until the platform kills it, which on a chat turn means a\n * wedged reply. `withFetchTimeout` bounds every call with a wall-clock deadline.\n *\n * It also defaults every request to `cache: 'no-store'`. These clients run\n * inside Next.js route handlers, whose patched `fetch` caches GETs by default —\n * keyed on URL ONLY, so a per-user header like `X-Chat-User` does not vary the\n * cache key. Left uncorrected, that means one user's hosted-backend response\n * (their conversation list, their config) can be served straight to another\n * user, and a since-updated history can be served stale. Per-user data must\n * never enter that cache, so the wrapper opts every wrapped call out of it;\n * callers that legitimately want caching can still override via `init`.\n */\n\nimport 'server-only';\n\n/** Default per-request timeout (ms) for the hosted HTTP clients. */\nexport const DEFAULT_HTTP_TIMEOUT_MS = 30_000;\n\n/**\n * Wrap a `fetch` implementation so every call is bounded by a wall-clock\n * timeout. Returns a `fetch`-compatible function: when the deadline passes the\n * underlying request is aborted (the promise rejects with an `AbortError`), so a\n * hung/stalled upstream can never hold a request open indefinitely. A\n * caller-supplied `init.signal` is still honoured (aborting it aborts the\n * request). `timeoutMs <= 0` disables the timeout and returns the impl unchanged.\n *\n * Portable across Node and Edge/Workers — uses only `AbortController` /\n * `setTimeout` / `fetch`, no runtime-specific APIs.\n */\nexport function withFetchTimeout(fetchImpl: typeof fetch, timeoutMs: number): typeof fetch {\n  if (!timeoutMs || timeoutMs <= 0) return fetchImpl;\n  const wrapped = (\n    input: Parameters<typeof fetch>[0],\n    init?: Parameters<typeof fetch>[1],\n  ): Promise<Response> => {\n    const controller = new AbortController();\n    const callerSignal = init?.signal;\n    if (callerSignal) {\n      if (callerSignal.aborted) controller.abort();\n      else callerSignal.addEventListener('abort', () => controller.abort(), { once: true });\n    }\n    const timer = setTimeout(() => controller.abort(), timeoutMs);\n    return Promise.resolve(\n      fetchImpl(input, { cache: 'no-store', ...init, signal: controller.signal }),\n    ).finally(() => clearTimeout(timer));\n  };\n  return wrapped as typeof fetch;\n}\n"],"mappings":"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","names":[]}