{"_id":"@achref_hasni/membridge","name":"@achref_hasni/membridge","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.1":{"name":"@achref_hasni/membridge","version":"0.1.1","description":"The vendor-neutral TypeScript interface and benchmark harness for AI agent memory. Write once, swap memory backends (Mem0, Zep, Letta) with a config change, and get honest latency, cost, and recall comparisons for your LLM agents.","license":"Apache-2.0","author":{"name":"Achref Hasni"},"repository":{"type":"git","url":"git+https://github.com/AchrefHASNI/membridge.git"},"homepage":"https://github.com/AchrefHASNI/membridge#readme","bugs":{"url":"https://github.com/AchrefHASNI/membridge/issues"},"type":"module","engines":{"node":">=20"},"packageManager":"pnpm@9.15.0","keywords":["ai","ai-agents","agent-memory","llm","llm-memory","memory","long-term-memory","rag","retrieval","vector-database","knowledge-graph","mem0","zep","letta","memgpt","benchmark","adapter","vendor-neutral","typescript","openai","anthropic","claude","chatbot","context-engineering"],"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","bin":{"membridge":"dist/cli/index.js"},"publishConfig":{"access":"public"},"scripts":{"build":"tsup","dev":"tsup --watch","prepublishOnly":"pnpm run typecheck && pnpm test && pnpm build","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","lint":"biome check .","lint:fix":"biome check --write .","format":"biome format --write .","audit":"pnpm audit"},"dependencies":{"commander":"15.0.0","zod":"4.4.3"},"peerDependencies":{"@getzep/zep-cloud":">=3","@letta-ai/letta-client":">=1","mem0ai":">=3"},"peerDependenciesMeta":{"mem0ai":{"optional":true},"@getzep/zep-cloud":{"optional":true},"@letta-ai/letta-client":{"optional":true}},"devDependencies":{"@biomejs/biome":"2.5.0","@types/node":"26.0.0","mem0ai":"3.0.9","tsup":"8.5.1","typescript":"6.0.3","vitest":"4.1.9"},"_id":"@achref_hasni/membridge@0.1.1","gitHead":"f1aa2e3677fb04fccce917248cb71f10ad974afc","_nodeVersion":"22.18.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-7p1Iprw6ldo9RQEWtmckzKgI6Mo0HsSNEbF4uTincBLvZS3CUEIkLu8IsRfck7HgNLA0eeMrqlc7KByv/kNSjA==","shasum":"44c8e2bf1bdf6846e0ac5e44eecdbdd451e1f3e8","tarball":"https://registry.npmjs.org/@achref_hasni/membridge/-/membridge-0.1.1.tgz","fileCount":18,"unpackedSize":189103,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCbXwjDRdThdP+ge9kI20zPQwCRPHsPrskobNVDbEpISAIhAJ2eYVxfkEpXdwvHJOUPhNM14jCxGVwpQGu0OJ8Egih+"}]},"_npmUser":{"name":"achref_hasni","email":"achref11hasni@gmail.com"},"directories":{},"maintainers":[{"name":"achref_hasni","email":"achref11hasni@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/membridge_0.1.1_1784850557589_0.6645069029000417"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-23T23:49:17.469Z","0.1.1":"2026-07-23T23:49:17.733Z","modified":"2026-07-23T23:49:17.955Z"},"maintainers":[{"name":"achref_hasni","email":"achref11hasni@gmail.com"}],"description":"The vendor-neutral TypeScript interface and benchmark harness for AI agent memory. Write once, swap memory backends (Mem0, Zep, Letta) with a config change, and get honest latency, cost, and recall comparisons for your LLM agents.","homepage":"https://github.com/AchrefHASNI/membridge#readme","keywords":["ai","ai-agents","agent-memory","llm","llm-memory","memory","long-term-memory","rag","retrieval","vector-database","knowledge-graph","mem0","zep","letta","memgpt","benchmark","adapter","vendor-neutral","typescript","openai","anthropic","claude","chatbot","context-engineering"],"repository":{"type":"git","url":"git+https://github.com/AchrefHASNI/membridge.git"},"author":{"name":"Achref Hasni"},"bugs":{"url":"https://github.com/AchrefHASNI/membridge/issues"},"license":"Apache-2.0","readme":"<p align=\"center\">\n  <img src=\"./assets/logo.svg\" alt=\"MemBridge\" width=\"440\">\n</p>\n\n<p align=\"center\">\n  <strong>The vendor-neutral interface &amp; benchmark harness for AI agent memory.</strong><br>\n  Write once. Swap backends with a config change. Compare them honestly.\n</p>\n\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@achref_hasni/membridge\"><img src=\"https://img.shields.io/npm/v/@achref_hasni/membridge?color=6366F1&label=npm\" alt=\"npm version\"></a>\n  <a href=\"./LICENSE\"><img src=\"https://img.shields.io/badge/license-Apache--2.0-22D3EE\" alt=\"license\"></a>\n  <img src=\"https://img.shields.io/badge/types-included-3178C6?logo=typescript&logoColor=white\" alt=\"TypeScript\">\n  <img src=\"https://img.shields.io/badge/node-%E2%89%A520-339933?logo=node.js&logoColor=white\" alt=\"node >=20\">\n  <img src=\"https://img.shields.io/badge/status-v0.1.1%20on%20npm-A855F7\" alt=\"status\">\n</p>\n\n---\n\n## Table of contents\n\n- [What MemBridge is (and is not)](#what-membridge-is-and-is-not)\n- [Why MemBridge](#why-membridge)\n- [Quickstart](#quickstart)\n- [Installation](#installation)\n- [The contract](#the-contract)\n- [Configuration](#configuration)\n- [Environment variables](#environment-variables)\n- [API reference](#api-reference)\n- [CLI reference](#cli-reference)\n- [Architecture](#architecture)\n- [Writing a driver](#writing-a-driver)\n- [Testing &amp; replay cassettes](#testing--replay-cassettes)\n- [Benchmark (roadmap)](#benchmark-roadmap)\n- [Milestone status](#milestone-status)\n- [Development](#development)\n- [Troubleshooting](#troubleshooting)\n- [Contributing](#contributing)\n- [License](#license)\n\n---\n\n## What MemBridge is (and is not)\n\n**MemBridge** is the missing abstraction layer for **AI agent memory**. The ecosystem is crowded with incompatible backends — [Mem0](https://mem0.ai), [Zep](https://www.getzep.com), [Letta](https://letta.com) (MemGPT) — each with its own SDK, storage model, and cost profile. MemBridge gives you **one small, stable API** over all of them, so your **LLM agent**, **RAG pipeline**, or **chatbot** is not welded to a single vendor.\n\n| MemBridge **is** | MemBridge **is not** |\n|------------------|----------------------|\n| A vendor-neutral `MemoryDriver` interface | A vector database or embedding model |\n| A driver registry + adapter pattern | A hosted memory store |\n| A benchmark harness (M3–M4, planned) | A knowledge graph engine |\n| An escape hatch to raw vendor SDKs | A replacement for backend-specific features |\n\n> **npm package name:** `@achref_hasni/membridge` — the unscoped name `membridge` is blocked by npm (too similar to the existing [`mem-bridge`](https://www.npmjs.com/package/mem-bridge) package). The CLI command is still `membridge`.\n\n> **M1 reality check:** Only the **Mem0** driver is implemented today. Zep and Letta are declared as optional peer dependencies and documented for M2 — swapping to them in config will fail until those drivers land.\n\n---\n\n## Why MemBridge\n\n- **One interface, any backend.** `add` / `search` / `get` / `delete` over a normalized memory model. Switch backends by editing config — zero call-site changes (once the target driver exists).\n- **Honest benchmarks (planned).** Same dataset, same machine, same workload for every driver. Reports p50 **and** p95 (never just averages) and cost in dollars with the pricing assumption stated.\n- **No lock-in, no hidden features.** Every driver exposes `.raw()` — the real vendor client — so a backend's signature feature is always one call away.\n- **Built for trust.** Strict TypeScript, readable source, pinned deps, and recorded **cassettes** in tests so driver behavior is reproducible without a live vendor account.\n\n---\n\n## Quickstart\n\n### 1. Install\n\n```bash\npnpm add @achref_hasni/membridge mem0ai\n# mem0ai is the Mem0 SDK — install only the backend(s) you use\n```\n\n### 2. Set your API key\n\n```bash\ncp .env.example .env\n# Edit .env and set MEM0_API_KEY=...\n```\n\nOr pass the key inline in config (see [Configuration](#configuration)).\n\n### 3. Use in code\n\n```ts\nimport { createMemory } from '@achref_hasni/membridge';\n\nconst memory = await createMemory({\n  drivers: [{ name: 'mem0', options: { apiKey: process.env.MEM0_API_KEY } }],\n});\n\nawait memory.add({\n  userId: 'alice',\n  messages: [{ role: 'user', content: 'I always want a window seat' }],\n});\n\nconst hits = await memory.search({ userId: 'alice', query: 'seating preference', limit: 5 });\nconsole.log(hits[0]?.content); // \"Prefers a window seat\"  (score: 0.91)\n```\n\n### 4. Verify the CLI\n\n```bash\npnpm build\nnode dist/cli/index.js list\n# Registered drivers:\n#   - mem0\n```\n\n---\n\n## Installation\n\n### Package managers\n\n```bash\n# pnpm (recommended — matches packageManager in package.json)\npnpm add @achref_hasni/membridge mem0ai\n\n# npm\nnpm install @achref_hasni/membridge mem0ai\n\n# yarn\nyarn add @achref_hasni/membridge mem0ai\n```\n\n### Peer dependencies (optional backends)\n\nMemBridge declares backend SDKs as **optional peer dependencies**. You only install what you use:\n\n| Driver | npm package | Status |\n|--------|-------------|--------|\n| `mem0` | `mem0ai` (>=3) | ✅ M1 |\n| `zep` | `@getzep/zep-cloud` (>=3) | ⏳ M2 |\n| `letta` | `@letta-ai/letta-client` (>=1) | ⏳ M2 |\n\nIf you reference a driver name that is not registered, `createMemory()` throws a `MemBridgeError` with `op: 'resolve'`.\n\n### Requirements\n\n- **Node.js** >= 20\n- **TypeScript** projects: types ship with the package (`dist/index.d.ts`)\n\n---\n\n## The contract\n\nEvery driver implements this interface. Keep call sites against `MemoryDriver` — never against vendor SDKs directly (unless you intentionally use the escape hatch).\n\n```ts\ninterface MemoryDriver {\n  readonly name: string;\n  add(input: { userId: string; messages: { role: string; content: string }[] }): Promise<MemoryRecord[]>;\n  search(input: { userId: string; query: string; limit?: number }): Promise<MemoryRecord[]>;\n  get(input: { userId: string; limit?: number }): Promise<MemoryRecord[]>;\n  delete(input: { userId: string; id?: string }): Promise<void>;\n  raw(): unknown; // escape hatch — underlying vendor client\n}\n```\n\n### Normalized `MemoryRecord`\n\nAll drivers return the same shape:\n\n```ts\ninterface MemoryRecord {\n  id: string;\n  userId: string;\n  content: string;\n  metadata?: Record<string, unknown>;\n  createdAt: string;   // ISO-8601\n  score?: number;      // populated by search(); higher = more relevant\n}\n```\n\n---\n\n## Configuration\n\n### Programmatic config (supported today)\n\nThis is the **only** config path implemented in M1:\n\n```ts\nimport { createMemory, defineConfig } from '@achref_hasni/membridge';\n\n// Inline object — simplest path\nconst memory = await createMemory({\n  drivers: [{ name: 'mem0', options: { apiKey: process.env.MEM0_API_KEY } }],\n});\n\n// With defaultDriver when multiple backends are configured (M2+)\nconst cfg = defineConfig({\n  drivers: [\n    { name: 'mem0', options: { apiKey: process.env.MEM0_API_KEY } },\n    // { name: 'zep', options: { apiKey: process.env.ZEP_API_KEY } },  // M2\n  ],\n  defaultDriver: 'mem0',\n});\nconst mem = await createMemory(cfg);\n```\n\n`defineConfig()` is a typed identity helper — it gives editors full type-checking but does not load files.\n\n### Config file loading\n\nMemBridge discovers and loads `membridge.config.json`, `.js`, or `.mjs` from the current directory:\n\n```bash\nmembridge validate              # auto-discover config in cwd\nmembridge validate -c ./my.json # explicit path\n```\n\nFrom code:\n\n```ts\nimport { loadConfig, loadConfigFile } from '@achref_hasni/membridge';\n\nconst { config, configPath } = await loadConfig();\n// or\nconst cfg = await loadConfigFile('./membridge.config.json');\n```\n\nTypeScript config files (`membridge.config.ts`) are not loaded at runtime — import them from your app and pass the object to `createMemory()`.\n\n### Config schema\n\nValidated with [Zod](https://zod.dev). Invalid config throws `ZodError` at parse time.\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| `drivers` | `{ name, options }[]` | yes (min 1) | Registered driver names + driver-specific options |\n| `defaultDriver` | `string` | no | Which driver `createMemory()` picks when no override is passed. Must match a `drivers[].name`. |\n| `bench` | object | no | Benchmark settings (schema only in M1; no runner yet) |\n| `bench.dataset` | `string` | no | Path to benchmark dataset file |\n| `bench.costPer1kTokens` | `number` | no | USD per 1k tokens for cost reporting |\n| `bench.replay` | `boolean` | no | Default `false`. Run against cassettes instead of live backends (M3+) |\n\n### Mem0 driver options\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `apiKey` | `string` | `process.env.MEM0_API_KEY` | Mem0 platform API key |\n| `host` | `string` | `process.env.MEM0_HOST` | Custom host (self-hosted / proxy) |\n| `organizationId` | `string` | — | Mem0 organization ID |\n| `projectId` | `string` | — | Mem0 project ID |\n| `client` | `unknown` | — | Inject a fake/live client (tests & replay). Skips SDK construction. |\n\n### Swap the backend (when the driver exists)\n\n```diff\n  const memory = await createMemory({\n-   drivers: [{ name: 'mem0', options: { apiKey: process.env.MEM0_API_KEY } }],\n+   drivers: [{ name: 'zep',  options: { apiKey: process.env.ZEP_API_KEY } }],\n  });\n// Call sites using MemoryDriver stay identical\n```\n\n---\n\n## Environment variables\n\nCopy [`.env.example`](./.env.example) to `.env`. Never commit real keys.\n\n| Variable | Used by | Status |\n|----------|---------|--------|\n| `MEM0_API_KEY` | Mem0 driver | ✅ Read when `options.apiKey` is omitted |\n| `MEM0_HOST` | Mem0 driver | ✅ Read when `options.host` is omitted |\n| `ZEP_API_KEY` | Zep driver | ⏳ M2 |\n| `LETTA_API_KEY` | Letta driver | ⏳ M2 |\n| `LETTA_BASE_URL` | Letta driver | ⏳ M2 |\n\nMemBridge does **not** load `.env` automatically. Use `dotenv` or your framework's env handling, or pass keys in config.\n\n---\n\n## API reference\n\n### `createMemory(config, driverName?)`\n\nResolves a ready-to-use `MemoryDriver` from config.\n\n```ts\nimport { createMemory } from '@achref_hasni/membridge';\n\nconst memory = await createMemory(config);\nconst memory2 = await createMemory(config, 'mem0'); // explicit override\n```\n\n**Resolution order for driver name:**\n\n1. `driverName` argument (if provided)\n2. `config.defaultDriver`\n3. First entry in `config.drivers`\n\nThrows `MemBridgeError` (`op: 'resolve'`) if the chosen name is not in `config.drivers` or not registered in the registry.\n\n### `raw<T>(driver)`\n\nEscape hatch — returns the underlying vendor client with your type parameter:\n\n```ts\nimport { raw } from '@achref_hasni/membridge';\nimport type MemoryClient from 'mem0ai';\n\nconst client = raw<MemoryClient>(memory);\nawait client.users(); // Mem0-specific call MemBridge does not wrap\n```\n\n### `parseConfig(input)` / `defineConfig(config)`\n\nValidate or author config objects. `parseConfig` throws `ZodError` on invalid input.\n\n### `registry` / `DriverRegistry`\n\nThe default `registry` is pre-loaded with built-in drivers (currently `mem0`). Register custom drivers:\n\n```ts\nimport { DriverRegistry, type DriverFactory } from '@achref_hasni/membridge';\n\nconst custom = new DriverRegistry();\ncustom.register('my-backend', myFactory as DriverFactory);\nconst driver = await custom.resolve('my-backend', { /* options */ });\n```\n\nImporting `@achref_hasni/membridge` registers built-in drivers on the **global** `registry` as a side effect.\n\n### Errors — `MemBridgeError`\n\nAll driver operations wrap failures in a consistent shape:\n\n```ts\nclass MemBridgeError extends Error {\n  readonly driver: string;   // e.g. \"mem0\"\n  readonly op: 'add' | 'search' | 'get' | 'delete' | 'init' | 'resolve';\n  override readonly cause: unknown; // original backend error, preserved\n}\n```\n\nConfig validation throws **`ZodError`**, not `MemBridgeError` — a known inconsistency at the config seam.\n\n---\n\n## CLI reference\n\nAfter `pnpm build`, the `membridge` binary is at `dist/cli/index.js` (also linked via `package.json` `bin` when installed globally).\n\n### `membridge list`\n\nLists registered driver names.\n\n```bash\nmembridge list\n# Registered drivers:\n#   - mem0\n```\n\n### `membridge validate`\n\nValidates a config file and checks that every named driver is registered.\n\n```bash\nmembridge validate\nmembridge validate -c ./membridge.config.json\n```\n\n### `membridge bench`\n\nPrints the **benchmark comparison table preview** (placeholder metrics until M3–M4). Exits with code 1 until the runner ships.\n\n```bash\nmembridge bench\n```\n\n```\n┌──────────┬────────┬───────────┬───────────┬──────────────┬─────────────┐\n│ driver   │ recall │ p50 (ms)  │ p95 (ms)  │ $ / 1k convo │ footprint   │\n├──────────┼────────┼───────────┼───────────┼──────────────┼─────────────┤\n│ mem0     │ —      │ —         │ —         │ —            │ —           │\n└──────────┴────────┴───────────┴───────────┴──────────────┴─────────────┘\n```\n\n### `membridge parse`\n\nValidate an inline JSON config string (for scripting):\n\n```bash\nmembridge parse '{\"drivers\":[{\"name\":\"mem0\"}]}'\n```\n\n---\n\n## Architecture\n\n```\nsrc/\n├── index.ts           # Public entry: createMemory(), re-exports, driver registration\n├── config.ts          # Zod schemas + defineConfig / parseConfig\n├── core/\n│   ├── types.ts       # MemoryDriver contract + normalized types\n│   ├── registry.ts    # DriverRegistry (name → factory)\n│   ├── errors.ts      # MemBridgeError + wrapOp()\n│   └── escape-hatch.ts\n├── drivers/\n│   └── mem0/index.ts  # Mem0 adapter (normalization, lazy SDK import)\n└── cli/index.ts       # Commander CLI\n```\n\n**Request flow for `memory.add()`:**\n\n```\ncreateMemory() → parseConfig() → registry.resolve() → mem0Factory() → Mem0Driver.add() → wrapOp() → mem0ai client\n```\n\nSee [`CONTEXT.md`](./CONTEXT.md) for domain glossary and milestone definitions.\n\n---\n\n## Writing a driver\n\nEach new driver (M2+) must:\n\n1. Implement `MemoryDriver` — all four CRUD methods plus `raw()`\n2. Register a `DriverFactory` under a stable name (e.g. `'zep'`)\n3. Validate driver-specific options with Zod\n4. Wrap backend errors with `wrapOp()` → `MemBridgeError`\n5. Normalize responses to `MemoryRecord`\n6. Lazy-import the vendor SDK so unused backends stay out of the install tree\n7. Support `options.client` injection for recorded tests\n8. Include a cassette-based integration test\n\nMinimal skeleton:\n\n```ts\nimport { wrapOp } from '@achref_hasni/membridge';\nimport type { DriverFactory, MemoryDriver } from '@achref_hasni/membridge';\n\nexport const myFactory: DriverFactory = async (options) => {\n  const client = options.client ?? await createLiveClient(options);\n  return {\n    name: 'my-backend',\n    add: (input) => wrapOp('my-backend', 'add', () => /* ... */),\n    search: (input) => wrapOp('my-backend', 'search', () => /* ... */),\n    get: (input) => wrapOp('my-backend', 'get', () => /* ... */),\n    delete: (input) => wrapOp('my-backend', 'delete', () => /* ... */),\n    raw: () => client,\n  };\n};\n```\n\n---\n\n## Testing & replay cassettes\n\nTests use an injectable client instead of live API calls:\n\n```ts\nconst memory = await createMemory({\n  drivers: [{ name: 'mem0', options: { client: fakeMem0Client } }],\n});\n```\n\nRecorded fixtures live in `test/fixtures/*.cassette.json`. See `test/mem0.test.ts` for the full pattern.\n\nRun tests:\n\n```bash\npnpm test          # single run\npnpm test:watch    # watch mode\n```\n\nCurrent coverage (14 tests):\n\n- Config validation (`parseConfig`)\n- Registry register / list / resolve / unknown driver\n- Escape hatch (`raw()`)\n- Mem0 CRUD normalization, request forwarding, error wrapping, missing API key\n\n**Not yet tested:** CLI commands, `createMemory` edge cases (missing driver in config), live SDK import path, bench/replay engine.\n\n---\n\n## Benchmark (roadmap)\n\nThe launch artifact — one command, one reproducible comparison table — lands in M3–M4:\n\n```bash\nmembridge bench   # not yet available\n```\n\nPlanned output:\n\n```\n┌──────────┬────────┬───────────┬───────────┬──────────────┬─────────────┐\n│ driver   │ recall │ p50 (ms)  │ p95 (ms)  │ $ / 1k convo │ footprint   │\n├──────────┼────────┼───────────┼───────────┼──────────────┼─────────────┤\n│ mem0     │  …     │   …       │   …       │   …          │   …         │\n│ zep      │  …     │   …       │   …       │   …          │   …         │\n│ letta    │  …     │   …       │   …       │   …          │   …         │\n└──────────┴────────┴───────────┴───────────┴──────────────┴─────────────┘\n```\n\nEvery report will record hardware, driver versions, and the pricing assumption. Replay mode (`bench.replay: true`) will re-run against cassettes for CI without live keys.\n\n---\n\n## Milestone status\n\n| Milestone | Scope | State |\n|-----------|-------|-------|\n| **M1** | Core contract, driver registry, zod config, **Mem0 driver** + recorded test | ✅ done |\n| **M2** | **Zep** and **Letta** drivers behind the same interface | ⏳ next |\n| **M3** | Benchmark runner + metrics (latency percentiles, $ cost, recall) | ◻️ planned |\n| **M4** | `membridge bench` → markdown + JSON comparison table | ◻️ planned |\n| **M5** | Docs site + comparison table as README hero | ◻️ planned |\n\n---\n\n## Development\n\n### Setup\n\n```bash\ngit clone https://github.com/AchrefHASNI/membridge.git\ncd membridge\npnpm install\ncp .env.example .env   # optional — only needed for live Mem0 calls\n```\n\n### Scripts\n\n| Script | Command | Description |\n|--------|---------|-------------|\n| `build` | `pnpm build` | Bundle ESM + CJS + types via tsup |\n| `dev` | `pnpm dev` | Watch mode rebuild |\n| `test` | `pnpm test` | Vitest single run |\n| `typecheck` | `pnpm typecheck` | `tsc --noEmit` |\n| `lint` | `pnpm lint` | Biome check |\n| `lint:fix` | `pnpm lint:fix` | Biome auto-fix |\n| `format` | `pnpm format` | Biome format |\n| `audit` | `pnpm audit` | Dependency audit |\n\n### Quality gate (CI-equivalent)\n\n```bash\npnpm typecheck && pnpm test && pnpm lint && pnpm build\n```\n\n### Tech stack\n\nTypeScript (strict) · Node ≥ 20 · pnpm · [tsup](https://tsup.egoist.dev) · [Vitest](https://vitest.dev) · [Biome](https://biomejs.dev) · [Zod](https://zod.dev) · [Commander](https://github.com/tj/commander.js)\n\n### Project layout\n\n| Path | Purpose |\n|------|---------|\n| `src/` | Library source |\n| `test/` | Vitest tests + cassettes |\n| `dist/` | Build output (published to npm) |\n| `website/` | Marketing/docs Vite app (not published in npm `files`) |\n| `CONTEXT.md` | Domain glossary |\n| `.env.example` | Environment variable template |\n\n---\n\n## Troubleshooting\n\n### `unknown driver \"zep\"` (or `letta`)\n\nOnly `mem0` is registered in M1. Zep and Letta drivers arrive in M2.\n\n### `init failed: the \"mem0ai\" package is not installed`\n\n```bash\npnpm add mem0ai\n```\n\n### `missing Mem0 API key`\n\nSet `MEM0_API_KEY` in your environment or pass `options.apiKey` in config. Empty string counts as missing.\n\n### `Cannot find package 'commander'` when running CLI locally\n\nRun `pnpm install` first, then `pnpm build`. The CLI depends on runtime deps from `node_modules`.\n\n### Config throws `ZodError`\n\nCheck that `drivers` is a non-empty array and `defaultDriver` (if set) matches a driver name.\n\n### `MEM0_HOST` in `.env` has no effect\n\nEnsure you load `.env` into `process.env` (MemBridge does not call dotenv). Then `MEM0_HOST` is used when `options.host` is omitted. Or pass host explicitly:\n\n```ts\n{ name: 'mem0', options: { apiKey: '...', host: 'https://your-mem0-host' } }\n```\n\n---\n\n## Contributing\n\nDriver requests and PRs welcome. Each new driver needs:\n\n- The four contract methods + `.raw()`\n- Normalized errors (`MemBridgeError`)\n- A recorded integration test with cassette fixture\n- Lazy optional SDK import\n- Justification for any new dependency\n\nKeep the dependency tree small. See [Writing a driver](#writing-a-driver).\n\n---\n\n## Author\n\nBuilt by **Achref Hasni**.\n\n## License\n\n[Apache-2.0](./LICENSE) © 2026 Achref Hasni — chosen for the patent grant and maximum adoption.\n","readmeFilename":"README.md","_rev":"1-4d1e8296abfa66bd70c988beab646139"}