Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88e9f3973e | ||
|
|
4176293cc9 | ||
|
|
64487cbf31 | ||
|
|
d97b7e9131 | ||
|
|
c5bf46c44c | ||
|
|
ec5baaba20 | ||
|
|
12bb2588f0 | ||
|
|
8a9915d3fe | ||
|
|
789122bbea |
@@ -27,7 +27,7 @@ This is a **polyglot monorepo** containing Python and TypeScript packages, CLIs,
|
||||
| `server/` | FastAPI REST server for self-hosted Mem0 (Docker: FastAPI + PostgreSQL/pgvector + Neo4j) |
|
||||
| `openmemory/` | Self-hosted memory platform — `api/` (FastAPI + Alembic + MCP server) and `ui/` (Next.js 15 + React 19) |
|
||||
| `mem0-plugin/` | AI editor plugins (Claude Code, Cursor, Codex) — MCP server connection, lifecycle hooks, skills |
|
||||
| `skills/` | Claude Code skill definitions. Reference skills (SDK knowledge, always-on): `mem0/`, `mem0-cli/`, `mem0-vercel-ai-sdk/`. Pipeline skills (run on demand): `mem0-integrate/`, `mem0-test-integration/` |
|
||||
| `skills/` | Claude Code skill definitions — `mem0/`, `mem0-cli/`, `mem0-vercel-ai-sdk/` |
|
||||
| `docs/` | Documentation site (Mintlify) |
|
||||
| `tests/` | Python SDK tests (pytest) |
|
||||
| `evaluation/` | Benchmarking framework — LOCOMO evals, experiment runner, score generation |
|
||||
@@ -387,9 +387,7 @@ Model Context Protocol support in multiple places:
|
||||
### Plugin & Skills System
|
||||
|
||||
- `mem0-plugin/` provides integrations for Claude Code, Cursor, and Codex via MCP server connections and lifecycle hooks for automatic memory capture.
|
||||
- `skills/` contains structured skill definitions for AI agents, split into two categories:
|
||||
- **Reference skills** (always-on SDK knowledge): `mem0` (Python + TS SDKs, framework integrations), `mem0-cli` (terminal workflows), `mem0-vercel-ai-sdk` (Vercel AI provider).
|
||||
- **Pipeline skills** (run on demand): `mem0-integrate` wires Mem0 into an existing repo via a TDD pipeline; `mem0-test-integration` verifies what the integrator produced on the same branch. The two are loosely coupled via `.mem0-integration/` artifacts.
|
||||
- `skills/` contains structured skill definitions for AI agents, covering SDK usage, CLI workflows, and Vercel AI SDK patterns.
|
||||
|
||||
### Adding a New Provider
|
||||
|
||||
|
||||
@@ -147,27 +147,6 @@ mem0 search "What does Alice prefer?" --user-id alice
|
||||
|
||||
See the [CLI documentation](https://docs.mem0.ai/platform/cli) for the full command reference.
|
||||
|
||||
### Agent Skills
|
||||
|
||||
Teach your AI coding assistant (Claude Code, Codex, Cursor, Windsurf, OpenCode, OpenClaw, and any tool that supports the skills standard) how to build with Mem0. Two categories:
|
||||
|
||||
**Reference skills — always on** (SDK knowledge loaded into the assistant's context):
|
||||
|
||||
```bash
|
||||
npx skills add https://github.com/mem0ai/mem0 --skill mem0
|
||||
npx skills add https://github.com/mem0ai/mem0 --skill mem0-cli
|
||||
npx skills add https://github.com/mem0ai/mem0 --skill mem0-vercel-ai-sdk
|
||||
```
|
||||
|
||||
**Pipeline skills — run on demand** (execute an end-to-end workflow in an existing repo):
|
||||
|
||||
```bash
|
||||
npx skills add https://github.com/mem0ai/mem0 --skill mem0-integrate
|
||||
npx skills add https://github.com/mem0ai/mem0 --skill mem0-test-integration
|
||||
```
|
||||
|
||||
Use `/mem0-integrate` to wire Mem0 into an existing repo via a test-first pipeline, then `/mem0-test-integration` to verify. See the [skills catalog](./skills/) or [Vibecoding with Mem0](https://docs.mem0.ai/vibecoding) for the full picture.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
Mem0 requires an LLM to function, with `gpt-5-mini` from OpenAI as the default. However, it supports a variety of LLMs; for details, refer to our [Supported LLMs documentation](https://docs.mem0.ai/components/llms/overview).
|
||||
|
||||
@@ -109,19 +109,6 @@ client.project.update(
|
||||
)
|
||||
```
|
||||
|
||||
#### Toggle Memory Decay
|
||||
|
||||
`decay` is a per-project boolean that turns on [Memory Decay](/platform/features/memory-decay) — a search-time ranking bias that reinforces recently-accessed memories and gently dampens stale ones. The flag is `false` by default; set it via the same project-update endpoint:
|
||||
|
||||
```bash cURL
|
||||
curl -X PATCH https://api.mem0.ai/api/v1/orgs/organizations/$ORG_ID/projects/$PROJECT_ID/ \
|
||||
-H "Authorization: Token $MEM0_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"decay": true}'
|
||||
```
|
||||
|
||||
The current state is returned on every project read (and supports `?fields=decay` for a minimal response). Toggling has no effect on stored memories, only on how v3 search ranks them.
|
||||
|
||||
### Delete Project
|
||||
|
||||
<Warning>
|
||||
|
||||
@@ -4,19 +4,6 @@ description: "Major product launches, headline features, and milestones for Mem0
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
<Update label="2026-05-08" description="Memory Decay">
|
||||
|
||||
**Memory Decay — Recently-Used Memories Surface Higher, Automatically**
|
||||
|
||||
Per-project search-time ranking bias that boosts recently-touched memories and gently dampens stale ones. Off by default; opt in per project via the `decay` field on the project endpoint, or via `client.project.update(decay=True)` in the SDKs (Python `v2.0.2` / TypeScript `v3.0.3`).
|
||||
|
||||
- **Soft bias, never a filter.** The scaling factor stays in `0.3×–1.5×`. Decay can reorder candidates but never zeros them out — anything that surfaced before decay can still surface after.
|
||||
- **Reinforcement loop.** Every memory returned in a search has its access history updated, so frequently-used facts naturally float to the top over time.
|
||||
- **Public score still clamped to `[0, 1]`.** Existing API contract preserved; no client-side changes needed.
|
||||
- **v3 search only**, fully reversible. See [Memory Decay docs](/platform/features/memory-decay).
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-04-14" description="Mem0 SDK v2.0.0 / v3.0.0">
|
||||
|
||||
**New Memory Algorithm — State-of-the-Art Accuracy at ~3-4x Lower Cost**
|
||||
|
||||
@@ -4,13 +4,6 @@ description: "Release notes for the Mem0 hosted platform — backend, dashboard,
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
<Update label="2026-05-04" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Memory Decay:** Per-project search-time ranking bias that boosts recently-used memories and gently dampens stale ones. Opt-in via `decay` on the project endpoint; off by default. The scaling factor stays in `0.3×–1.5×`, the public `score` remains clamped to `[0, 1]`, and the bias never filters a candidate out. See [Memory Decay docs](/platform/features/memory-decay).
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-04-16" description="">
|
||||
|
||||
**Improvements:**
|
||||
|
||||
@@ -7,20 +7,6 @@ mode: "wide"
|
||||
<Tabs>
|
||||
<Tab title="Python">
|
||||
|
||||
<Update label="2026-05-08" description="v2.0.2">
|
||||
|
||||
**Bug Fixes:**
|
||||
- **Telemetry:** Stitch OSS and platform PostHog identities on `MemoryClient` init so `$identify` events fire and a single user is no longer tracked as two or three disconnected personas ([#5040](https://github.com/mem0ai/mem0/pull/5040))
|
||||
- **Security:** Harden against SQL injection and prompt injection ([#4997](https://github.com/mem0ai/mem0/pull/4997))
|
||||
|
||||
**New Features:**
|
||||
- **SDK:** Expose `decay` on `project.update` ([#5062](https://github.com/mem0ai/mem0/pull/5062))
|
||||
|
||||
**Improvements:**
|
||||
- **Plugin:** Hand `mem0` search decisions to the agent ([#4992](https://github.com/mem0ai/mem0/pull/4992))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-04-25" description="v2.0.1">
|
||||
|
||||
**Bug Fixes:**
|
||||
@@ -924,18 +910,6 @@ See the [OSS v1 to v2 migration guide](https://docs.mem0.ai/migration/oss-v1-to-
|
||||
</Tab>
|
||||
|
||||
<Tab title="TypeScript">
|
||||
<Update label="2026-05-08" description="v3.0.3">
|
||||
|
||||
**Bug Fixes:**
|
||||
- **Telemetry:** Stitch OSS and platform PostHog identities on `MemoryClient` init so `$identify` events fire and a single user is no longer tracked as two or three disconnected personas ([#5040](https://github.com/mem0ai/mem0/pull/5040))
|
||||
- **Vector Stores:** Fix inverted vector distance in PGVector implementation ([#4944](https://github.com/mem0ai/mem0/pull/4944))
|
||||
- **Security:** Harden against SQL injection and prompt injection ([#4997](https://github.com/mem0ai/mem0/pull/4997))
|
||||
|
||||
**New Features:**
|
||||
- **SDK:** Expose `decay` on `project.update` ([#5062](https://github.com/mem0ai/mem0/pull/5062))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-04-25" description="v3.0.2">
|
||||
|
||||
**Bug Fixes:**
|
||||
|
||||
+1
-2
@@ -83,8 +83,7 @@
|
||||
"platform/advanced-memory-operations",
|
||||
"platform/features/criteria-retrieval",
|
||||
"platform/features/contextual-add",
|
||||
"platform/features/custom-instructions",
|
||||
"platform/features/memory-decay"
|
||||
"platform/features/custom-instructions"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -187,7 +187,6 @@ If the user is on a pre-current major (Python < 2, TS < 3, or Platform `output_f
|
||||
- [Criteria-Based Retrieval](https://docs.mem0.ai/platform/features/criteria-retrieval) [Platform]: Use when targeting memories by custom criteria, not just semantic similarity.
|
||||
- [Contextual Add](https://docs.mem0.ai/platform/features/contextual-add) [Platform]: Use when `add()` should consider the surrounding conversation, not just the latest turn.
|
||||
- [Custom Instructions](https://docs.mem0.ai/platform/features/custom-instructions) [Platform]: Use when tailoring what Mem0 extracts and stores on Platform.
|
||||
- [Memory Decay](https://docs.mem0.ai/platform/features/memory-decay) [Platform]: Use when search results should boost recently-reinforced memories and dampen stale ones — opt-in per project, search-time only, never filters candidates out.
|
||||
- [Advanced Memory Operations](https://docs.mem0.ai/platform/advanced-memory-operations) [Platform]: Use when basic CRUD is not enough - batch ops, complex filters, workflows.
|
||||
|
||||
### Features - Data Management
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
---
|
||||
title: Memory Decay
|
||||
description: "Boost recently-used memories and gently dampen stale ones at search time, without filtering anything out."
|
||||
---
|
||||
|
||||
# Memory Decay
|
||||
|
||||
Older memories drift in relevance at different speeds. A user's coffee order matters every morning; a one-off project name from last quarter rarely matters again. Memory Decay makes that intuition explicit at search time: every time a memory is returned in a search it gets a small reinforcement, and memories that haven't been touched in a while have their ranking score gently dampened.
|
||||
|
||||
It is **a soft ranking bias, never a filter.** Decay never zeroes a candidate out — at worst it scales its score by `0.3×`. Anything that would have surfaced without decay can still surface with decay on, just with a different ranking among similarly-scored results.
|
||||
|
||||
<Info>
|
||||
**Use Memory Decay when…**
|
||||
- Search results are crowded with old facts the user no longer cares about.
|
||||
- You want recently-used memories to drift to the top automatically — without writing custom scoring logic.
|
||||
- You want this preference applied per project so cohorts can be compared side-by-side.
|
||||
</Info>
|
||||
|
||||
<Warning>
|
||||
Memory Decay is **opt-in per project** and **off by default**. Search behavior is bit-identical to today until you turn it on. The toggle applies to v3 search only.
|
||||
</Warning>
|
||||
|
||||
## How it works
|
||||
|
||||
Every memory carries a small piece of bookkeeping: when was it last retrieved, and how often. Memory Decay turns that history into a *scaling factor* in the range `0.3×` to `1.5×` and multiplies it into the ranking score at search time.
|
||||
|
||||
| Memory state | Scaling factor | Ranking effect |
|
||||
|---|---|---|
|
||||
| Just accessed | ≈ **1.5×** | Strong boost |
|
||||
| Touched today | 1.2 – 1.4× | Mild boost |
|
||||
| Idle for a few days | 0.6 – 1.0× | Mild dampening |
|
||||
| Idle for weeks | 0.4 – 0.6× | Stronger dampening |
|
||||
| Idle for many months / years | ≈ **0.3×** | Floor — never lower |
|
||||
|
||||
The bounds matter: `0.3` is the floor and `1.5` is the ceiling, so decay can meaningfully reorder candidates without ever dominating the underlying relevance score.
|
||||
|
||||
At search time the pipeline:
|
||||
|
||||
1. Widens the candidate pool (`top_k × 3`, with a floor of 50) so reordering has room.
|
||||
2. Multiplies each candidate's score by its scaling factor.
|
||||
3. Sorts on the unclamped product so the full `0.3×–1.5×` range can rearrange candidates.
|
||||
4. Returns the public `score` clamped to `[0, 1]` so the API contract is preserved.
|
||||
5. Truncates to the `top_k` you requested.
|
||||
6. Records a fire-and-forget reinforcement against each returned memory — its access history grows by one, capped at the most recent 20 touches.
|
||||
|
||||
Memories created before decay was enabled don't yet have an access history. They use a sensible fallback: their `updated_at` is treated as a single past touch, so the same scale above applies based on how stale that update is — a recently-updated legacy memory enters near the neutral band, a long-stale one sits closer to the floor. Once surfaced in a search after decay is on, they accumulate access history naturally and behave like any other memory.
|
||||
|
||||
## Configure access
|
||||
|
||||
- Set `MEM0_API_KEY` in your environment, or pass it to the SDK constructor.
|
||||
- Initialize the client with the organization and project you want to scope to.
|
||||
|
||||
The toggle lives on the project. You enable decay by patching the project's `decay` field; everything else — your `add` calls, your `search` calls, your application code — stays exactly the same.
|
||||
|
||||
## Enable decay for a project
|
||||
|
||||
### 1. Turn the flag on
|
||||
|
||||
The toggle is exposed on the standard project-update endpoint, the same place where `multilingual` and `custom_categories` live.
|
||||
|
||||
<CodeGroup>
|
||||
```bash cURL
|
||||
curl -X PATCH https://api.mem0.ai/api/v1/orgs/organizations/$ORG_ID/projects/$PROJECT_ID/ \
|
||||
-H "Authorization: Token $MEM0_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"decay": true}'
|
||||
```
|
||||
|
||||
```python Python
|
||||
import os
|
||||
import requests
|
||||
|
||||
org_id = os.environ["MEM0_ORG_ID"]
|
||||
project_id = os.environ["MEM0_PROJECT_ID"]
|
||||
|
||||
requests.patch(
|
||||
f"https://api.mem0.ai/api/v1/orgs/organizations/{org_id}/projects/{project_id}/",
|
||||
headers={"Authorization": f"Token {os.environ['MEM0_API_KEY']}"},
|
||||
json={"decay": True},
|
||||
)
|
||||
```
|
||||
|
||||
```javascript Node.js
|
||||
const res = await fetch(
|
||||
`https://api.mem0.ai/api/v1/orgs/organizations/${process.env.MEM0_ORG_ID}/projects/${process.env.MEM0_PROJECT_ID}/`,
|
||||
{
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
Authorization: `Token ${process.env.MEM0_API_KEY}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ decay: true }),
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
```json Response
|
||||
{ "message": "Updated decay" }
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### 2. Confirm the state
|
||||
|
||||
`decay` is returned on every project read. To fetch only this field, use `?fields=decay`.
|
||||
|
||||
<CodeGroup>
|
||||
```bash cURL
|
||||
curl "https://api.mem0.ai/api/v1/orgs/organizations/$ORG_ID/projects/$PROJECT_ID/?fields=decay" \
|
||||
-H "Authorization: Token $MEM0_API_KEY"
|
||||
```
|
||||
|
||||
```json Response
|
||||
{ "decay": true }
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### 3. Turn it back off
|
||||
|
||||
The toggle is fully reversible. Setting it to `false` immediately restores the pre-decay ranking; nothing about your stored memories is modified or lost.
|
||||
|
||||
<CodeGroup>
|
||||
```bash cURL
|
||||
curl -X PATCH https://api.mem0.ai/api/v1/orgs/organizations/$ORG_ID/projects/$PROJECT_ID/ \
|
||||
-H "Authorization: Token $MEM0_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"decay": false}'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<Note>
|
||||
The toggle is idempotent. Re-applying the same value is a no-op, and access history accumulated while decay was on is preserved if you flip it back on later.
|
||||
</Note>
|
||||
|
||||
## What changes when decay is on
|
||||
|
||||
- **Search ranking reorders.** A relevant memory you reinforced an hour ago will tend to outrank an equally-relevant memory that was last touched a month ago.
|
||||
- **The candidate pool over-fetches** to give the scaling factor room to reorder. You still get exactly the `top_k` you requested, but the items returned can come from a deeper slice of the pre-decay ranking than before.
|
||||
- **The public `score` field stays in `[0, 1]`.** Even when the internal product exceeds 1, the field returned to the client is clamped, so existing assertions and downstream UI logic continue to work.
|
||||
|
||||
## What stays the same
|
||||
|
||||
- **Public API shape** — every endpoint accepts the same parameters and returns the same fields. You don't touch your client code.
|
||||
- **Threshold semantics on the request side** — your `threshold` is still applied during candidate selection.
|
||||
- **Memory creation and storage** — every new memory still lands the same way. Decay is a search-time concern.
|
||||
- **Per-memory data** — categories, metadata, timestamps, embeddings: untouched.
|
||||
|
||||
<Warning>
|
||||
Because the scaling factor is applied *after* the threshold filter has already run, an item that passed the request `threshold` can come back with a public `score` slightly below it (a stale candidate dampened by `0.3×`). This is intentional — decay is a soft bias, not a filter. If you require a hard `score >= threshold` invariant on the response, filter client-side after the call.
|
||||
</Warning>
|
||||
|
||||
## Lifecycle of a memory under decay
|
||||
|
||||
| Stage | Scaling factor | Effect |
|
||||
|---|---|---|
|
||||
| Just added | ≈ 1.5× | Strong boost — fresh facts surface easily. |
|
||||
| Reinforced on a recent search | 1.2 – 1.5× | Sustains its boost for the next several searches. |
|
||||
| Idle for a few days | 0.6 – 1.0× | Falls back into the neutral band. |
|
||||
| Idle for weeks | 0.4 – 0.6× | Mild dampening — can still surface for strong matches. |
|
||||
| Pre-decay legacy memory (no access history) | 0.3 – 1.0× | Falls back to `updated_at`: recently-updated entries land near 1.0×, long-stale entries approach the 0.3× floor. |
|
||||
|
||||
The reinforcement is bounded: each memory tracks at most the last 20 access timestamps, so the boost stays well-behaved no matter how many times a memory is retrieved.
|
||||
|
||||
## FAQ
|
||||
|
||||
**Will decay ever drop a result that would otherwise surface?**
|
||||
No. The floor is `0.3×` — the scaling factor can dampen a score, never zero it. Threshold filtering happens *before* decay, so any candidate that cleared the threshold is in the pool decay reorders.
|
||||
|
||||
**Why is the public score sometimes below my requested threshold?**
|
||||
The threshold is applied to the candidate pool pre-decay; the scaling factor then reshapes scores in the `0.3×–1.5×` band. A stale-but-relevant candidate can come back with a final score slightly under your threshold by design — the candidate stays visible but visibly dampened. Filter client-side if you need a hard floor on the response.
|
||||
|
||||
**Does decay change how I add memories?**
|
||||
No. The `client.add(...)` path is unchanged. Decay is a search-time ranking adjustment.
|
||||
|
||||
**What if I had memories before turning decay on?**
|
||||
They use a fallback: the memory's `updated_at` is treated as a single historical touch, so the same scaling applies based on how stale that update is — a recently-updated legacy memory enters near the neutral band (~1.0×), a long-stale one closer to the floor (~0.3×). Once retrieved they accumulate access history and behave like any other memory.
|
||||
|
||||
**Can I tune how aggressively decay scales scores?**
|
||||
Not in this version. The current scaling is calibrated to be conservative — wide enough to meaningfully reorder candidates, narrow enough to never dominate the underlying relevance score. Per-project tuning is on the roadmap.
|
||||
|
||||
**Can I see the scaling factor per result?**
|
||||
Internal scoring details are persisted on the search Event for support and debugging. They aren't exposed in the public response by design — the response surface stays a single `score` field.
|
||||
|
||||
**Does decay interact with reranking?**
|
||||
Yes — they layer cleanly. The reranker produces a richer relevance score; decay then biases that score by reinforcement history before final truncation to `top_k`.
|
||||
|
||||
## What's next
|
||||
|
||||
This release is deliberately the simplest version of decay we could ship — every memory contributes to ranking through its access history alone, so the signal can be evaluated in isolation. On the roadmap:
|
||||
|
||||
- **Category-aware weighting.** A fact tagged `health` will be able to carry more weight than a passing observation tagged `misc`, so important categories don't get dampened the same way as noise.
|
||||
- **Auto-tuning per project.** Project-scoped automatic adjustment of how aggressively decay scales scores, based on observed access patterns — replacing the fixed scaling band with one that fits your workload.
|
||||
|
||||
Both extensions are forward-compatible — no migration on your side will be needed when they ship.
|
||||
+2
-24
@@ -22,35 +22,13 @@ We follow the llms.txt standard:
|
||||
|
||||
## Agent Skills
|
||||
|
||||
Mem0 ships two kinds of skills for AI coding assistants. Both work with Claude Code, Codex, Cursor, Windsurf, OpenCode, OpenClaw, and any assistant that supports the skills standard.
|
||||
|
||||
### Reference skills — always on
|
||||
|
||||
Teach your assistant Mem0's SDK surface so it writes correct code in everyday development:
|
||||
Teach your coding assistant how to build with Mem0:
|
||||
|
||||
```bash
|
||||
npx skills add https://github.com/mem0ai/mem0 --skill mem0
|
||||
npx skills add https://github.com/mem0ai/mem0 --skill mem0-cli
|
||||
npx skills add https://github.com/mem0ai/mem0 --skill mem0-vercel-ai-sdk
|
||||
```
|
||||
|
||||
- `mem0` — Python and TypeScript SDKs (Platform + OSS), plus framework integrations (LangChain, CrewAI, OpenAI Agents, LangGraph, LlamaIndex, etc.)
|
||||
- `mem0-cli` — terminal workflows for the `mem0` CLI (both Node and Python builds)
|
||||
- `mem0-vercel-ai-sdk` — `@mem0/vercel-ai-provider` and `createMem0`
|
||||
|
||||
### Pipeline skills — run on demand
|
||||
|
||||
Let your assistant execute an end-to-end workflow in an existing repo. Invoked as slash commands:
|
||||
|
||||
```bash
|
||||
npx skills add https://github.com/mem0ai/mem0 --skill mem0-integrate
|
||||
npx skills add https://github.com/mem0ai/mem0 --skill mem0-test-integration
|
||||
```
|
||||
|
||||
- `/mem0-integrate` — wire Mem0 into an existing repository using a goal-driven, test-first pipeline. Detects the stack, asks whether to use Platform or OSS, writes failing tests first, and keeps the integration additive and feature-flagged.
|
||||
- `/mem0-test-integration` — verify what `/mem0-integrate` produced. Runs the repo's native test suite and a real end-to-end smoke flow against your API key, then produces a scorecard.
|
||||
|
||||
See the [skills index](https://github.com/mem0ai/mem0/tree/main/skills) for the full catalog.
|
||||
Works with Claude Code, Cursor, Windsurf, and any assistant that supports skills. Once installed, your assistant understands Mem0's full API, framework integrations, and common patterns.
|
||||
|
||||
## MCP Server Setup
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CODEX_PLUGIN_ROOT}/scripts/on_user_prompt.sh",
|
||||
"statusMessage": "Checking memory relevance...",
|
||||
"statusMessage": "Searching mem0 memories...",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_user_prompt.sh",
|
||||
"statusMessage": "Checking memory relevance...",
|
||||
"statusMessage": "Searching mem0 memories...",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,65 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
# Hook: UserPromptSubmit
|
||||
#
|
||||
# Fires on every user message. Instead of pre-searching mem0 with the
|
||||
# raw prompt, this injects a decision rubric telling the agent when
|
||||
# and how to search itself. The agent has more context than this
|
||||
# script does -- let it decide.
|
||||
# Fires on every user message. Searches mem0 for relevant memories
|
||||
# and injects them into Claude's context before processing.
|
||||
#
|
||||
# Input: JSON on stdin (prompt, session_id, cwd, transcript_path)
|
||||
# Output: Decision rubric injected into Claude's context (exit 0)
|
||||
# Input: JSON on stdin with prompt, session_id, cwd, transcript_path
|
||||
# Output: Matching memories as context text (exit 0)
|
||||
#
|
||||
# Skips search for very short prompts (< 20 chars) and when
|
||||
# MEM0_API_KEY is not set. Uses a 3s timeout to minimize latency.
|
||||
|
||||
# Intentionally omit -e so the script always exits 0 even if jq fails --
|
||||
# must never block the user's prompt.
|
||||
# Intentionally omit -e so the script always exits 0 even if
|
||||
# curl or jq fail — must never block the user's prompt.
|
||||
set -uo pipefail
|
||||
|
||||
INPUT=$(cat)
|
||||
PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""' 2>/dev/null || echo "")
|
||||
|
||||
# Acknowledgements and short replies don't warrant memory context
|
||||
# Skip trivial prompts — not worth a network call
|
||||
if [ ${#PROMPT} -lt 20 ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# No API key means the agent can't search anyway
|
||||
if [ -z "${MEM0_API_KEY:-}" ]; then
|
||||
API_KEY="${MEM0_API_KEY:-}"
|
||||
if [ -z "$API_KEY" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
USER_ID="${MEM0_USER_ID:-${USER:-default}}"
|
||||
|
||||
cat <<EOF
|
||||
## Memory check
|
||||
# Build request body safely via jq to avoid injection
|
||||
BODY=$(jq -n --arg query "$PROMPT" --arg user_id "$USER_ID" \
|
||||
'{query: $query, filters: {user_id: $user_id}, top_k: 5}')
|
||||
|
||||
Before responding, decide whether persistent memory context from mem0 would
|
||||
improve your answer. The agent -- not this hook -- owns this decision.
|
||||
# Search mem0 for memories relevant to this prompt
|
||||
RESPONSE=$(curl -s --max-time 3 \
|
||||
-X POST "https://api.mem0.ai/v2/memories/search/" \
|
||||
-H "Authorization: Token $API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$BODY" \
|
||||
2>/dev/null || echo "")
|
||||
|
||||
**Search WHEN** the user:
|
||||
- references past work, decisions, or things "we" built
|
||||
- asks "how should we...", "best way to...", or any decision-style question
|
||||
- hits an error, bug, or asks for debugging help
|
||||
- requests work that touches their stack, tools, conventions, or preferences
|
||||
- starts a non-trivial task in a known project
|
||||
if [ -z "$RESPONSE" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
**Skip WHEN:**
|
||||
- the prompt is an acknowledgement or continuation
|
||||
- the user is *stating* new info -- that's a write trigger (\`add_memory\`), not a search
|
||||
- it's a pure syntax / factual question answerable from general knowledge
|
||||
- you already searched this scope earlier in the turn
|
||||
# Extract memories from response (API returns a flat array)
|
||||
MEMORIES=$(echo "$RESPONSE" | jq -r '
|
||||
if type == "array" then . else .results // [] end |
|
||||
if length == 0 then empty else
|
||||
"## Relevant memories from mem0\n\n" +
|
||||
(map(select(.memory != null) | "- " + .memory) | join("\n"))
|
||||
end
|
||||
' 2>/dev/null || echo "")
|
||||
|
||||
**If searching, do it well:**
|
||||
- Run **2-4 parallel** \`search_memories\` calls with different angles, not one
|
||||
query that echoes the user's prompt.
|
||||
- Phrase queries as **nouns** ("auth module decisions"), not full sentences.
|
||||
- Filter shape: the root must be a logical operator (\`AND\` / \`OR\` / \`NOT\`)
|
||||
with an array, and metadata uses a **nested** object (not dotted keys).
|
||||
Combine \`user_id\` with one \`metadata.type\` clause per call:
|
||||
- \`{"AND": [{"user_id": "$USER_ID"}, {"metadata": {"type": "decision"}}]}\` -- design / architecture
|
||||
- \`{"AND": [{"user_id": "$USER_ID"}, {"metadata": {"type": "anti_pattern"}}]}\` -- debugging, error handling
|
||||
- \`{"AND": [{"user_id": "$USER_ID"}, {"metadata": {"type": "user_preference"}}]}\` -- tooling, stack, style
|
||||
- \`{"AND": [{"user_id": "$USER_ID"}, {"metadata": {"type": "convention"}}]}\` -- established patterns
|
||||
- Or scope with just \`{"AND": [{"user_id": "$USER_ID"}]}\` when no metadata filter fits.
|
||||
- Empty results are normal -- proceed without context.
|
||||
EOF
|
||||
if [ -n "$MEMORIES" ]; then
|
||||
echo "$MEMORIES"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
name: mem0-codex
|
||||
description: >
|
||||
Mem0 persistent memory integration for Codex. Automatically retrieve relevant
|
||||
memories at the start of each task, store key learnings when tasks complete,
|
||||
and capture session state before context is lost. Use the mem0 MCP tools
|
||||
(add_memory, search_memories, get_memories, etc.) for all memory operations.
|
||||
---
|
||||
|
||||
# Mem0 Memory Protocol for Codex
|
||||
|
||||
You have access to persistent memory via the mem0 MCP tools. Follow this protocol to maintain context across sessions.
|
||||
|
||||
## On every new task
|
||||
|
||||
1. Call `search_memories` with a query related to the current task or project to load relevant context.
|
||||
2. Review returned memories to understand what has been learned in prior sessions.
|
||||
3. If appropriate, call `get_memories` to browse all stored memories for this user.
|
||||
|
||||
## After completing significant work
|
||||
|
||||
Extract key learnings and store them using the `add_memory` tool:
|
||||
|
||||
- **Decisions made** -> Include metadata `{"type": "decision"}`
|
||||
- **Strategies that worked** -> Include metadata `{"type": "task_learning"}`
|
||||
- **Failed approaches** -> Include metadata `{"type": "anti_pattern"}`
|
||||
- **User preferences observed** -> Include metadata `{"type": "user_preference"}`
|
||||
- **Environment/setup discoveries** -> Include metadata `{"type": "environmental"}`
|
||||
- **Conventions established** -> Include metadata `{"type": "convention"}`
|
||||
|
||||
Memories can be as detailed as needed -- include full context, reasoning, code snippets, file paths, and examples. Longer, searchable memories are more valuable than vague one-liners.
|
||||
|
||||
## Before losing context
|
||||
|
||||
If context is about to be compacted or the session is ending, store a comprehensive session summary:
|
||||
|
||||
```
|
||||
## Session Summary
|
||||
|
||||
### User's Goal
|
||||
[What the user originally asked for]
|
||||
|
||||
### What Was Accomplished
|
||||
[Numbered list of tasks completed]
|
||||
|
||||
### Key Decisions Made
|
||||
[Architectural choices, trade-offs discussed]
|
||||
|
||||
### Files Created or Modified
|
||||
[Important file paths with what changed]
|
||||
|
||||
### Current State
|
||||
[What is in progress, pending items, next steps]
|
||||
```
|
||||
|
||||
Include metadata: `{"type": "session_state"}`
|
||||
|
||||
## Memory hygiene
|
||||
|
||||
- Do NOT write to MEMORY.md or any file-based memory. Use mem0 MCP tools exclusively.
|
||||
- Only store genuinely useful learnings. Skip trivial interactions.
|
||||
- Use specific, searchable language in memory content.
|
||||
@@ -1,131 +0,0 @@
|
||||
---
|
||||
name: mem0-mcp
|
||||
description: >
|
||||
Mem0 memory protocol for agents using the mem0 MCP tools (Claude Code, Cursor,
|
||||
Codex, and any other MCP-aware runtime). Decide deliberately when memory context
|
||||
would help, run targeted searches with metadata filters when it would, and store
|
||||
key learnings as work completes. Use the mem0 MCP tools (add_memory,
|
||||
search_memories, get_memories, etc.) for all memory operations.
|
||||
---
|
||||
|
||||
# Mem0 MCP Memory Protocol
|
||||
|
||||
You have access to persistent memory via the mem0 MCP tools. Follow this protocol to maintain context across sessions.
|
||||
|
||||
## On every new task
|
||||
|
||||
Decide whether persistent memory context would improve your response, then act accordingly. Don't search by default — search deliberately.
|
||||
|
||||
### Decide: search or skip?
|
||||
|
||||
**Search WHEN** the user:
|
||||
- references past work, decisions, or things "we" built
|
||||
- asks "how should we...", "best way to...", or any decision-style question
|
||||
- hits an error, bug, or asks for debugging help
|
||||
- requests work that touches their stack, tools, conventions, or preferences
|
||||
- starts a non-trivial task in a known project
|
||||
|
||||
**Skip WHEN:**
|
||||
- the prompt is an acknowledgement or continuation ("ok", "thanks", "continue")
|
||||
- the user is *stating* new info — that's a write trigger (`add_memory`), not a search
|
||||
- it's a pure syntax / factual question answerable from general knowledge
|
||||
- you already searched this scope earlier in the turn
|
||||
|
||||
Empty results are normal. Proceed without context — they don't mean the system is broken.
|
||||
|
||||
### How to search well
|
||||
|
||||
When you do search, run **2–4 parallel** `search_memories` calls at different angles instead of one query echoing the user's prompt.
|
||||
|
||||
**Query phrasing:**
|
||||
- Use **nouns**, not sentences. `"auth module decisions"` beats `"what did we decide about auth"`.
|
||||
- Strip conversational filler. *"remember when we picked Postgres?"* → search `"Postgres choice"`.
|
||||
- Use entity names, not pronouns. Resolve "that thing" from recent context first.
|
||||
- Don't search on meta-questions ("what was that?") — use recent context or `get_memories` ordered by `created_at`.
|
||||
|
||||
**Metadata filters** match the same `type` values written under "After completing significant work" below.
|
||||
|
||||
Two rules from the v2 filter spec:
|
||||
|
||||
1. The root **must** be a logical operator (`AND` / `OR` / `NOT`) with an array. A bare `{"user_id": "..."}` won't work.
|
||||
2. Metadata uses a **nested** object, not a dotted key. `{"metadata": {"type": "decision"}}`, never `{"metadata.type": "decision"}`. Only top-level metadata keys are filterable.
|
||||
|
||||
Combine `user_id` with one metadata clause per call:
|
||||
|
||||
| `metadata.type` clause | Use for |
|
||||
|--------|---------|
|
||||
| `{"metadata": {"type": "decision"}}` | design / architecture / "how should we" questions |
|
||||
| `{"metadata": {"type": "anti_pattern"}}` | debugging, error handling, things that failed before |
|
||||
| `{"metadata": {"type": "user_preference"}}` | tooling, stack, style — always include for code work |
|
||||
| `{"metadata": {"type": "convention"}}` | established patterns in this project |
|
||||
|
||||
Full filter (replace `<your_user_id>` with the active user_id from your runtime):
|
||||
```python
|
||||
filters={"AND": [{"user_id": "<your_user_id>"}, {"metadata": {"type": "decision"}}]}
|
||||
```
|
||||
|
||||
### Worked example
|
||||
|
||||
User asks: *"Refactor the auth module to use JWT."*
|
||||
|
||||
Don't:
|
||||
```python
|
||||
search_memories(query="Refactor the auth module to use JWT")
|
||||
# Hits whatever shares words. Misses prior decisions and preferences.
|
||||
```
|
||||
|
||||
Do (parallel — substitute the active `user_id` for `<your_user_id>`):
|
||||
```python
|
||||
search_memories(query="auth module decisions",
|
||||
filters={"AND": [{"user_id": "<your_user_id>"}, {"metadata": {"type": "decision"}}]})
|
||||
search_memories(query="JWT",
|
||||
filters={"AND": [{"user_id": "<your_user_id>"}]})
|
||||
search_memories(query="auth refactor failures",
|
||||
filters={"AND": [{"user_id": "<your_user_id>"}, {"metadata": {"type": "anti_pattern"}}]})
|
||||
search_memories(query="auth",
|
||||
filters={"AND": [{"user_id": "<your_user_id>"}, {"metadata": {"type": "user_preference"}}]})
|
||||
```
|
||||
|
||||
## After completing significant work
|
||||
|
||||
Extract key learnings and store them using the `add_memory` tool:
|
||||
|
||||
- **Decisions made** -> Include metadata `{"type": "decision"}`
|
||||
- **Strategies that worked** -> Include metadata `{"type": "task_learning"}`
|
||||
- **Failed approaches** -> Include metadata `{"type": "anti_pattern"}`
|
||||
- **User preferences observed** -> Include metadata `{"type": "user_preference"}`
|
||||
- **Environment/setup discoveries** -> Include metadata `{"type": "environmental"}`
|
||||
- **Conventions established** -> Include metadata `{"type": "convention"}`
|
||||
|
||||
Memories can be as detailed as needed -- include full context, reasoning, code snippets, file paths, and examples. Longer, searchable memories are more valuable than vague one-liners.
|
||||
|
||||
## Before losing context
|
||||
|
||||
If context is about to be compacted or the session is ending, store a comprehensive session summary:
|
||||
|
||||
```
|
||||
## Session Summary
|
||||
|
||||
### User's Goal
|
||||
[What the user originally asked for]
|
||||
|
||||
### What Was Accomplished
|
||||
[Numbered list of tasks completed]
|
||||
|
||||
### Key Decisions Made
|
||||
[Architectural choices, trade-offs discussed]
|
||||
|
||||
### Files Created or Modified
|
||||
[Important file paths with what changed]
|
||||
|
||||
### Current State
|
||||
[What is in progress, pending items, next steps]
|
||||
```
|
||||
|
||||
Include metadata: `{"type": "session_state"}`
|
||||
|
||||
## Memory hygiene
|
||||
|
||||
- Do NOT write to MEMORY.md or any file-based memory. Use mem0 MCP tools exclusively.
|
||||
- Only store genuinely useful learnings. Skip trivial interactions.
|
||||
- Use specific, searchable language in memory content.
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mem0ai",
|
||||
"version": "3.0.3",
|
||||
"version": "3.0.2",
|
||||
"description": "The Memory Layer For Your AI Apps",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
|
||||
@@ -134,7 +134,7 @@ export default class MemoryClient {
|
||||
captureClientEvent("init", this, {
|
||||
client_type: "MemoryClient",
|
||||
}).catch((error: any) => {
|
||||
console.error("Failed to capture event:", error);
|
||||
console.debug("Failed to capture event:", error);
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error("Failed to initialize client:", error);
|
||||
@@ -165,7 +165,7 @@ export default class MemoryClient {
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("Failed to alias telemetry identity:", error);
|
||||
console.debug("Failed to alias telemetry identity:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ export default class MemoryClient {
|
||||
args_count: args.length,
|
||||
keys: args.length > 0 ? args[0] : [],
|
||||
}).catch((error: any) => {
|
||||
console.error("Failed to capture event:", error);
|
||||
console.debug("Failed to capture event:", error);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -50,13 +50,6 @@ export interface PromptUpdatePayload {
|
||||
memoryDepth?: string | null;
|
||||
usecaseSetting?: string | number;
|
||||
multilingual?: boolean;
|
||||
/**
|
||||
* Toggle Memory Decay for this project. When `true`, search-time ranking
|
||||
* boosts recently-used memories and gently dampens stale ones; when `false`,
|
||||
* ranking is restored to the pre-decay behaviour. Off by default.
|
||||
* See https://docs.mem0.ai/platform/features/memory-decay
|
||||
*/
|
||||
decay?: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,12 +64,12 @@ class UnifiedTelemetry implements TelemetryClient {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("Telemetry event capture failed:", await response.text());
|
||||
console.debug("Telemetry event capture failed:", response.status);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Telemetry event capture failed:", error);
|
||||
console.debug("Telemetry event capture failed:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -99,15 +99,16 @@ class UnifiedTelemetry implements TelemetryClient {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(
|
||||
"Telemetry identify capture failed:",
|
||||
await response.text(),
|
||||
);
|
||||
return false;
|
||||
console.debug("Telemetry identify capture failed:", response.status);
|
||||
}
|
||||
// Any HTTP response (2xx / 4xx / 5xx) marks the pair aliased.
|
||||
// PostHog person-merge is idempotent at the person level, so a
|
||||
// duplicate $identify on a recovered server is harmless. Only a
|
||||
// genuine network failure (no response) leaves the marker absent
|
||||
// so the next init can retry.
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Telemetry identify capture failed:", error);
|
||||
console.debug("Telemetry identify capture failed:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,12 +467,18 @@ export class Memory {
|
||||
this.telemetryId === "anonymous" ||
|
||||
this.telemetryId === "anonymous-supabase"
|
||||
) {
|
||||
this.telemetryId =
|
||||
(await getOrCreateMem0UserId()) ||
|
||||
(await this.vectorStore.getUserId());
|
||||
try {
|
||||
await this.vectorStore.setUserId(this.telemetryId);
|
||||
} catch {}
|
||||
const existing = await this.vectorStore.getUserId();
|
||||
if (existing && !existing.startsWith("anonymous")) {
|
||||
// Preserve a real vector-store UUID so prior PostHog events stay
|
||||
// tied to this user's identity. Cross-surface stitching with
|
||||
// CLI/Python OSS for these existing users is deferred.
|
||||
this.telemetryId = existing;
|
||||
} else {
|
||||
this.telemetryId = (await getOrCreateMem0UserId()) || existing;
|
||||
try {
|
||||
await this.vectorStore.setUserId(this.telemetryId);
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
return this.telemetryId;
|
||||
} catch (error) {
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ def _maybe_alias_anon_to_email(user_email):
|
||||
if client_telemetry.capture_identify(anon_id, user_email):
|
||||
mark_aliased(anon_id, user_email)
|
||||
except Exception as e:
|
||||
logger.debug("Failed to alias anon telemetry to %r: %s", user_email, e)
|
||||
logger.debug("Failed to alias anon telemetry: %s", e)
|
||||
|
||||
|
||||
class MemoryClient:
|
||||
|
||||
+2
-16
@@ -398,7 +398,6 @@ class Project(BaseProject):
|
||||
custom_categories: Optional[List[str]] = None,
|
||||
retrieval_criteria: Optional[List[Dict[str, Any]]] = None,
|
||||
multilingual: Optional[bool] = None,
|
||||
decay: Optional[bool] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Update project settings.
|
||||
@@ -408,9 +407,6 @@ class Project(BaseProject):
|
||||
custom_categories: New categories for the project
|
||||
retrieval_criteria: New retrieval criteria for the project
|
||||
multilingual: Whether to use the input language for memory storage and retrieval
|
||||
decay: Toggle Memory Decay for this project. When True, search-time
|
||||
ranking boosts recently-used memories and gently dampens stale ones; when
|
||||
False, ranking is restored to the pre-decay behaviour. Off by default.
|
||||
|
||||
Returns:
|
||||
Dictionary containing the API response.
|
||||
@@ -427,12 +423,11 @@ class Project(BaseProject):
|
||||
and custom_categories is None
|
||||
and retrieval_criteria is None
|
||||
and multilingual is None
|
||||
and decay is None
|
||||
):
|
||||
raise ValueError(
|
||||
"At least one parameter must be provided for update: "
|
||||
"custom_instructions, custom_categories, retrieval_criteria, "
|
||||
"multilingual, decay"
|
||||
"multilingual"
|
||||
)
|
||||
|
||||
payload = self._prepare_params(
|
||||
@@ -441,7 +436,6 @@ class Project(BaseProject):
|
||||
"custom_categories": custom_categories,
|
||||
"retrieval_criteria": retrieval_criteria,
|
||||
"multilingual": multilingual,
|
||||
"decay": decay,
|
||||
}
|
||||
)
|
||||
response = self._client.patch(
|
||||
@@ -457,7 +451,6 @@ class Project(BaseProject):
|
||||
"custom_categories": custom_categories,
|
||||
"retrieval_criteria": retrieval_criteria,
|
||||
"multilingual": multilingual,
|
||||
"decay": decay,
|
||||
"sync_type": "sync",
|
||||
},
|
||||
)
|
||||
@@ -722,7 +715,6 @@ class AsyncProject(BaseProject):
|
||||
custom_categories: Optional[List[str]] = None,
|
||||
retrieval_criteria: Optional[List[Dict[str, Any]]] = None,
|
||||
multilingual: Optional[bool] = None,
|
||||
decay: Optional[bool] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Update project settings.
|
||||
@@ -732,9 +724,6 @@ class AsyncProject(BaseProject):
|
||||
custom_categories: New categories for the project
|
||||
retrieval_criteria: New retrieval criteria for the project
|
||||
multilingual: Whether to use the input language for memory storage and retrieval
|
||||
decay: Toggle Memory Decay for this project. When True, search-time
|
||||
ranking boosts recently-used memories and gently dampens stale ones; when
|
||||
False, ranking is restored to the pre-decay behaviour. Off by default.
|
||||
|
||||
Returns:
|
||||
Dictionary containing the API response.
|
||||
@@ -751,12 +740,11 @@ class AsyncProject(BaseProject):
|
||||
and custom_categories is None
|
||||
and retrieval_criteria is None
|
||||
and multilingual is None
|
||||
and decay is None
|
||||
):
|
||||
raise ValueError(
|
||||
"At least one parameter must be provided for update: "
|
||||
"custom_instructions, custom_categories, retrieval_criteria, "
|
||||
"multilingual, decay"
|
||||
"multilingual"
|
||||
)
|
||||
|
||||
payload = self._prepare_params(
|
||||
@@ -765,7 +753,6 @@ class AsyncProject(BaseProject):
|
||||
"custom_categories": custom_categories,
|
||||
"retrieval_criteria": retrieval_criteria,
|
||||
"multilingual": multilingual,
|
||||
"decay": decay,
|
||||
}
|
||||
)
|
||||
response = await self._client.patch(
|
||||
@@ -781,7 +768,6 @@ class AsyncProject(BaseProject):
|
||||
"custom_categories": custom_categories,
|
||||
"retrieval_criteria": retrieval_criteria,
|
||||
"multilingual": multilingual,
|
||||
"decay": decay,
|
||||
"sync_type": "async",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -114,20 +114,29 @@ class AnonymousTelemetry:
|
||||
_logger.debug("Failed to capture telemetry event %r: %s", event_name, e)
|
||||
|
||||
def capture_identify(self, anon_id, email):
|
||||
"""Fire $identify with $anon_distinct_id so PostHog merges anon_id into email."""
|
||||
"""Fire $identify synchronously so the bool return reflects delivery.
|
||||
|
||||
Returns True only when the SDK returned a non-None UUID. False
|
||||
when telemetry is disabled, when the sync client cannot be
|
||||
constructed, when the SDK returns None (e.g. @no_throw caught
|
||||
an HTTP error), or when capture() raises.
|
||||
"""
|
||||
if self.posthog is None:
|
||||
return False
|
||||
if not anon_id or not email or anon_id == email:
|
||||
return False
|
||||
sync_client = _get_sync_posthog()
|
||||
if sync_client is None:
|
||||
return False
|
||||
try:
|
||||
self.posthog.capture(
|
||||
result = sync_client.capture(
|
||||
distinct_id=email,
|
||||
event="$identify",
|
||||
properties={"$anon_distinct_id": anon_id, "client_source": "python"},
|
||||
)
|
||||
return True
|
||||
return result is not None
|
||||
except Exception as e:
|
||||
_logger.debug("Failed to capture $identify for %r: %s", email, e)
|
||||
_logger.debug("Failed to capture $identify: %s", e)
|
||||
return False
|
||||
|
||||
def close(self):
|
||||
@@ -177,6 +186,41 @@ def _shutdown_oss_telemetry():
|
||||
_oss_telemetry_instance = None
|
||||
|
||||
|
||||
# ─── Sync PostHog client for $identify only ──────────────────────────────
|
||||
# $identify must give an honest delivery signal so we only persist an alias
|
||||
# marker when PostHog actually accepted the event. The default async client
|
||||
# enqueues on a background thread, so capture() returns a UUID immediately
|
||||
# regardless of HTTP outcome. sync_mode=True bypasses the queue and either
|
||||
# raises (posthog<7) or returns None (posthog>=7 via @no_throw) on failure.
|
||||
_sync_posthog_instance = None
|
||||
_sync_posthog_lock = threading.Lock()
|
||||
|
||||
|
||||
def _get_sync_posthog():
|
||||
"""Lazy-init a sync-mode PostHog client used only for $identify.
|
||||
|
||||
Returns None if telemetry is disabled or the constructor raises.
|
||||
"""
|
||||
global _sync_posthog_instance
|
||||
if not MEM0_TELEMETRY:
|
||||
return None
|
||||
if _sync_posthog_instance is not None:
|
||||
return _sync_posthog_instance
|
||||
with _sync_posthog_lock:
|
||||
if _sync_posthog_instance is not None:
|
||||
return _sync_posthog_instance
|
||||
try:
|
||||
_sync_posthog_instance = Posthog(
|
||||
project_api_key=PROJECT_API_KEY,
|
||||
host=HOST,
|
||||
sync_mode=True,
|
||||
)
|
||||
except Exception as e:
|
||||
_logger.debug("Failed to init sync PostHog client: %s", e)
|
||||
_sync_posthog_instance = None
|
||||
return _sync_posthog_instance
|
||||
|
||||
|
||||
# Module-level client telemetry singleton (used by capture_client_event).
|
||||
# No before_send — hosted MemoryClient traffic must never be sampled.
|
||||
client_telemetry = AnonymousTelemetry()
|
||||
|
||||
+1
-2
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "mem0ai"
|
||||
version = "2.0.2"
|
||||
version = "2.0.1"
|
||||
description = "Long-term memory for AI Agents"
|
||||
authors = [
|
||||
{ name = "Mem0", email = "support@mem0.ai" }
|
||||
@@ -153,4 +153,3 @@ known-first-party = ["mem0", "mem0_cli"]
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
known_first_party = ["mem0", "mem0_cli"]
|
||||
# isort scope kept aligned with [tool.ruff.lint.isort] above.
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
# Mem0 Skills for AI Coding Assistants
|
||||
|
||||
Mem0 ships structured skill definitions for Claude Code, Codex, Cursor, OpenCode, OpenClaw, and any assistant that supports the [skills standard](https://github.com/anthropic-experimental/skills). Skills teach the assistant how to work with Mem0 — either by loading SDK knowledge into context, or by executing an end-to-end workflow on demand.
|
||||
|
||||
## Two Categories
|
||||
|
||||
### Reference skills — always on
|
||||
|
||||
Installed once, loaded into context so the assistant writes correct Mem0 code. Use these for day-to-day development.
|
||||
|
||||
| Skill | Surface | Install |
|
||||
|-------|---------|---------|
|
||||
| [`mem0`](./mem0/) | Python + TypeScript SDKs (Platform + OSS), framework integrations | `npx skills add https://github.com/mem0ai/mem0 --skill mem0` |
|
||||
| [`mem0-cli`](./mem0-cli/) | Terminal workflows (`mem0` CLI, both Node and Python) | `npx skills add https://github.com/mem0ai/mem0 --skill mem0-cli` |
|
||||
| [`mem0-vercel-ai-sdk`](./mem0-vercel-ai-sdk/) | `@mem0/vercel-ai-provider` and `createMem0` | `npx skills add https://github.com/mem0ai/mem0 --skill mem0-vercel-ai-sdk` |
|
||||
|
||||
### Pipeline skills — run on demand
|
||||
|
||||
Invoked as a slash command to execute a specific end-to-end workflow. These do real work: they create branches, write tests, run code.
|
||||
|
||||
| Skill | Trigger | Install |
|
||||
|-------|---------|---------|
|
||||
| [`mem0-integrate`](./mem0-integrate/) | `/mem0-integrate` — wire Mem0 into an existing repo via TDD | `npx skills add https://github.com/mem0ai/mem0 --skill mem0-integrate` |
|
||||
| [`mem0-test-integration`](./mem0-test-integration/) | `/mem0-test-integration` — verify what `/mem0-integrate` produced | `npx skills add https://github.com/mem0ai/mem0 --skill mem0-test-integration` |
|
||||
|
||||
The two pipeline skills are designed to run in sequence on the same workspace:
|
||||
|
||||
```
|
||||
/mem0-integrate → mem0-integrate/<slug> branch + .mem0-integration/ artifacts
|
||||
/mem0-test-integration → scorecard (compile + runtime verification, real API smoke test)
|
||||
```
|
||||
|
||||
## Choosing a Skill
|
||||
|
||||
- **Writing Mem0 code in a new or existing project?** → `mem0`
|
||||
- **Using the terminal CLI?** → `mem0-cli`
|
||||
- **Building with `@ai-sdk/*`?** → `mem0-vercel-ai-sdk`
|
||||
- **Want the assistant to wire Mem0 into an existing repo for you?** → `mem0-integrate`, then `mem0-test-integration`
|
||||
|
||||
## Links
|
||||
|
||||
- [Vibecoding with Mem0](https://docs.mem0.ai/vibecoding) — canonical landing page
|
||||
- [Claude Code integration](https://docs.mem0.ai/integrations/claude-code)
|
||||
- [Mem0 Platform Dashboard](https://app.mem0.ai)
|
||||
- [Mem0 Documentation](https://docs.mem0.ai)
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -1,189 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but not
|
||||
limited to compiled object code, generated documentation, and
|
||||
conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work.
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by the Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding any notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2024 Mem0.ai
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,89 +0,0 @@
|
||||
# mem0-integrate — Pipeline Skill
|
||||
|
||||
Wire [Mem0](https://mem0.ai) into an existing repository end-to-end, using a goal-driven, test-first pipeline.
|
||||
|
||||
> **This is a pipeline skill, not a reference skill.** Invoke it as `/mem0-integrate` when you want your assistant to do the work of integrating Mem0 into a target repo. For day-to-day SDK coding help, install [`mem0`](../mem0/SKILL.md) instead.
|
||||
>
|
||||
> **Part of the Mem0 Skill Graph:**
|
||||
> - Reference: [mem0](../mem0/SKILL.md) · [mem0-cli](../mem0-cli/SKILL.md) · [mem0-vercel-ai-sdk](../mem0-vercel-ai-sdk/SKILL.md)
|
||||
> - Pipeline: **mem0-integrate** (this skill) → [mem0-test-integration](../mem0-test-integration/SKILL.md)
|
||||
|
||||
## What This Skill Does
|
||||
|
||||
When invoked, your assistant will:
|
||||
|
||||
- **Detect** the target repo's language and stack automatically
|
||||
- **Ask** whether to integrate with Mem0 Platform (managed) or Mem0 Open Source (self-hosted)
|
||||
- **Write failing tests first** — no implementation until tests exist
|
||||
- **Keep the integration additive and feature-flagged** — existing behavior stays byte-for-byte identical when the flag is unset
|
||||
- **Produce a local feature branch** (`mem0-integrate/...`) and a `.mem0-integration/` directory of artifacts (`goal.md`, `plan.md`, `product.json`) consumed by the companion verification skill
|
||||
|
||||
## When to Use
|
||||
|
||||
Trigger phrases:
|
||||
|
||||
- "Integrate Mem0 into this repo"
|
||||
- "Add Mem0 to my project"
|
||||
- "Wire Mem0 into `<repo>`"
|
||||
- "How do I add memory to an existing project?"
|
||||
|
||||
Do **not** use this skill for general SDK usage (install [`mem0`](../mem0/SKILL.md)), terminal workflows (install [`mem0-cli`](../mem0-cli/SKILL.md)), or Vercel AI SDK integration (install [`mem0-vercel-ai-sdk`](../mem0-vercel-ai-sdk/SKILL.md)).
|
||||
|
||||
## Installation
|
||||
|
||||
### CLI (Claude Code, Codex, OpenCode, OpenClaw, or any tool that supports skills)
|
||||
|
||||
```bash
|
||||
npx skills add https://github.com/mem0ai/mem0 --skill mem0-integrate
|
||||
```
|
||||
|
||||
For verification on the same branch, also install the companion skill:
|
||||
|
||||
```bash
|
||||
npx skills add https://github.com/mem0ai/mem0 --skill mem0-test-integration
|
||||
```
|
||||
|
||||
### Claude.ai
|
||||
|
||||
1. Download this `skills/mem0-integrate` folder as a ZIP
|
||||
2. Go to **Settings > Capabilities > Skills**
|
||||
3. Click **Upload skill** and select the ZIP
|
||||
|
||||
### Claude API (Skills API)
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.anthropic.com/v1/skills \
|
||||
-H "x-api-key: $ANTHROPIC_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name": "mem0-integrate", "source": "https://github.com/mem0ai/mem0/tree/main/skills/mem0-integrate"}'
|
||||
```
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- A Mem0 Platform API key ([get one](https://app.mem0.ai/dashboard/api-keys)) *or* a working OSS setup (LLM + vector store)
|
||||
- Python 3.10+ or Node.js 18+ in the target repo
|
||||
- A clean working tree on the target repo's default branch
|
||||
|
||||
## Workflow
|
||||
|
||||
```
|
||||
/mem0-integrate → creates mem0-integrate/<slug> branch,
|
||||
writes .mem0-integration/ artifacts,
|
||||
implements against failing tests
|
||||
/mem0-test-integration → runs the repo's native test suite,
|
||||
executes a real end-to-end smoke flow,
|
||||
produces a scorecard
|
||||
```
|
||||
|
||||
The two skills are loosely coupled — they share the same workspace and branch via `.mem0-integration/`, but the verifier never modifies source.
|
||||
|
||||
## Links
|
||||
|
||||
- [Mem0 Platform Dashboard](https://app.mem0.ai)
|
||||
- [Mem0 Documentation](https://docs.mem0.ai)
|
||||
- [Mem0 GitHub](https://github.com/mem0ai/mem0)
|
||||
- [Platform vs OSS comparison](https://docs.mem0.ai/platform/platform-vs-oss)
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -1,620 +0,0 @@
|
||||
---
|
||||
name: mem0-integrate
|
||||
description: >
|
||||
Integrate Mem0 into an existing repository using a goal-driven, TDD pipeline.
|
||||
Detects the repo's language automatically and asks the user to pick between
|
||||
Mem0 Platform (managed) and Mem0 Open Source (self-hosted). Writes failing
|
||||
tests before any implementation. Produces a local feature branch plus
|
||||
`.mem0-integration/` artifacts consumed by the paired verification skill.
|
||||
TRIGGER when: user says "integrate mem0", "add mem0 to this repo", "wire
|
||||
mem0 into <repo>", or asks how to add memory to an existing project.
|
||||
DO NOT TRIGGER when: the user wants general SDK usage (use skill:mem0),
|
||||
CLI usage (use skill:mem0-cli), or Vercel AI SDK (use skill:mem0-vercel-ai-sdk).
|
||||
After success, invoke skill:mem0-test-integration to verify in the same
|
||||
workspace (loose coupling).
|
||||
license: Apache-2.0
|
||||
metadata:
|
||||
author: mem0ai
|
||||
version: "0.1.0"
|
||||
category: ai-memory
|
||||
tags: "memory, integration, tdd, platform, oss"
|
||||
mem0_tested_versions: "mem0ai (PyPI) >=2.0.0,<3.0.0; mem0ai (npm) >=3.0.0,<4.0.0"
|
||||
---
|
||||
|
||||
# mem0-integrate
|
||||
|
||||
Wire Mem0 into an existing repo with a goal-driven, test-first pipeline.
|
||||
Pairs with `mem0-test-integration` for verification.
|
||||
|
||||
## Canonical sources (fetch before deciding anything)
|
||||
|
||||
The skill MUST `WebFetch` these URLs before step 3 and cite them in
|
||||
`plan.md`. They are the ground truth — do not rely on ambient knowledge
|
||||
of the Mem0 API.
|
||||
|
||||
### Agent-ready docs
|
||||
- Scope-tagged docs index: https://docs.mem0.ai/llms.txt
|
||||
- Full docs (single file, deep dives): https://docs.mem0.ai/llms-full.txt
|
||||
- OpenAPI spec (Platform REST, machine-readable): https://docs.mem0.ai/openapi.json
|
||||
- Hosted MCP server: https://mcp.mem0.ai (requires Platform API key)
|
||||
- Integrations index: https://docs.mem0.ai/integrations
|
||||
|
||||
### Published Mem0 skills — delegate; do not reimplement
|
||||
Prefer these over writing your own call-site patterns. Each is a
|
||||
standalone `SKILL.md` with triggers, examples, and version-pinned code.
|
||||
|
||||
- SDK (Python + TS, Platform + OSS): https://raw.githubusercontent.com/mem0ai/mem0/main/skills/mem0/SKILL.md
|
||||
- CLI: https://raw.githubusercontent.com/mem0ai/mem0/main/skills/mem0-cli/SKILL.md
|
||||
- Vercel AI SDK: https://raw.githubusercontent.com/mem0ai/mem0/main/skills/mem0-vercel-ai-sdk/SKILL.md
|
||||
- Editor/MCP plugin glue (9 MCP tools): https://github.com/mem0ai/mem0/tree/main/mem0-plugin
|
||||
|
||||
### SDK source (read when docs are ambiguous)
|
||||
Public repo. Cross-check against the `mem0_tested_versions` range in this
|
||||
skill's frontmatter if the `main` branch has moved past a major.
|
||||
|
||||
- Repo root: https://github.com/mem0ai/mem0
|
||||
- Python SDK: https://github.com/mem0ai/mem0/tree/main/mem0
|
||||
- TypeScript SDK: https://github.com/mem0ai/mem0/tree/main/mem0-ts
|
||||
|
||||
### Quickstarts (for bootstrapping unfamiliar stacks)
|
||||
- Platform: https://docs.mem0.ai/platform/quickstart
|
||||
- OSS Python: https://docs.mem0.ai/open-source/python-quickstart
|
||||
- OSS Node: https://docs.mem0.ai/open-source/node-quickstart
|
||||
- Platform vs OSS comparison: https://docs.mem0.ai/platform/platform-vs-oss
|
||||
|
||||
## Integration principles (non-negotiable)
|
||||
|
||||
The true goal of this skill is to produce a **PR the maintainers can accept
|
||||
without argument**. That rules out anything invasive.
|
||||
|
||||
1. **Additive, not replacing.** If the target repo already has a memory
|
||||
system, a session store, a user-context layer, or anything named
|
||||
`Memory` / `memory_*`, Mem0 sits **alongside** it, not in place of it.
|
||||
The existing system keeps working unchanged.
|
||||
2. **Opt-in by default.** Gate all new Mem0 code behind a feature flag
|
||||
(env var like `MEM0_ENABLED=1`, a config key, or a strategy selector).
|
||||
With the flag unset, behavior is the repo's original behavior,
|
||||
byte-for-byte.
|
||||
3. **No breakage.** No removed exports, no renamed public functions,
|
||||
no changed method signatures, no modified existing tests, no changed
|
||||
behavior of existing tests. All pre-existing tests must pass unchanged
|
||||
both with the flag set and unset.
|
||||
4. **Minimal dependency surface.** Add `mem0ai` (plus any deps the
|
||||
delegated skill requires) and nothing else. No new vector stores, no
|
||||
graph databases, no provider SDKs the repo does not already use.
|
||||
5. **Separable commits.** Code, tests, and config/docs land in separate
|
||||
commits so reviewers can cherry-pick.
|
||||
6. **The null hypothesis wins.** If no additive, gated fit exists after
|
||||
step 6 (plan), exit with code 1 and a rationale. A bad PR is worse
|
||||
than no PR.
|
||||
7. **Backend only.** Mem0 integration lives in server-side code. API keys,
|
||||
memory scope, and user-identity resolution are not safe client-side.
|
||||
If the repo has both backend and frontend, the call sites live in
|
||||
backend files. Frontend-only repos are rejected at preconditions.
|
||||
|
||||
Enforced at four gates: **preconditions** (reject frontend-only repos
|
||||
and repos where additive fit is impossible), **step 2 comprehension**
|
||||
(confirm a backend exists and name candidate surfaces), **step 6 plan
|
||||
review** (reject plans that mutate existing exports or name client-side
|
||||
call sites), and **step 10 self-healing loop** (refuse to "fix" principle
|
||||
violations — surface them instead).
|
||||
|
||||
## Skill delegation rules
|
||||
|
||||
Before writing any code, check whether a published skill already covers
|
||||
the target stack. If yes, delegate — copy its call-site pattern into
|
||||
`plan.md` and into the tests; do not paraphrase.
|
||||
|
||||
| Detected in target repo | Delegate to | Why |
|
||||
|---|---|---|
|
||||
| `@ai-sdk/*` + `ai` in `package.json` | `skills/mem0-vercel-ai-sdk` | Integration is via `createMem0` provider wrapper, not raw `MemoryClient`. |
|
||||
| CLI-only repo (Typer, Commander, Click, Cobra) with no LLM call sites | `skills/mem0-cli` | Call sites are command handlers, not model wrappers. Consider whether mem0 actually fits first. |
|
||||
| Target is an MCP client / editor config (Claude Code, Cursor, Codex settings) | `mem0-plugin` | Wire via MCP server URL + hooks; no SDK code usually needed. |
|
||||
| Any other Python or TS repo with an LLM call site | `skills/mem0` | Default SDK integration path. |
|
||||
|
||||
Record the delegated skill's raw URL in `plan.md` under a
|
||||
**"Delegated skill:"** field. The test writer in step 7 and the
|
||||
implementation subagent in step 8 both read this field.
|
||||
|
||||
## Preconditions
|
||||
|
||||
Refuse to start unless ALL of the following are true:
|
||||
|
||||
- Current working directory is inside a git repository with a clean index
|
||||
(no uncommitted changes). Protects the user's work — every edit lands on
|
||||
a feature branch, not on top of in-progress changes.
|
||||
- Repo has a detectable language (`package.json` / `pyproject.toml` /
|
||||
`requirements.txt`). No language → exit cleanly with a written rationale.
|
||||
- Repo has a **backend**. Detected by: a `backend/` or `server/` or `api/`
|
||||
directory; a Python package with FastAPI/Flask/Django/Starlette; a Node
|
||||
package with Express/Fastify/Koa/NestJS/Next-API-routes; an agent-loop
|
||||
framework (LangGraph, LangChain, LlamaIndex, Agno). Frontend-only repos
|
||||
(pure React/Vue/Svelte SPAs, static sites, mobile-only) → exit with
|
||||
code 1 and a rationale. Mem0 is not installed client-side.
|
||||
- The user has already decided Mem0 fits this repo. This skill does NOT
|
||||
survey the codebase to justify fit — bring a concrete goal. (Step 2
|
||||
*does* read the repo to understand what it does and locate backend
|
||||
integration surfaces; that is mechanics, not fit-justification.)
|
||||
|
||||
Exit with a written rationale if any precondition fails. Do not try to
|
||||
"make it work anyway."
|
||||
|
||||
## Pipeline
|
||||
|
||||
### 1. Language detection
|
||||
|
||||
| Signal | Track |
|
||||
|---|---|
|
||||
| `package.json` + TypeScript config | Node / TypeScript |
|
||||
| `package.json` (no TS config) | Node / JavaScript |
|
||||
| `pyproject.toml` or `requirements.txt` | Python |
|
||||
|
||||
Monorepo with both → ask which subdirectory to operate in, then recurse.
|
||||
|
||||
### 2. Repo comprehension — what does this repo do, and where is the backend?
|
||||
|
||||
Before any decision (product, goal, plan), understand the repo enough
|
||||
to locate *where in the backend* the integration belongs. This is not
|
||||
fit-surveying — the user already decided Mem0 fits. This is mechanics:
|
||||
you cannot write a plan without knowing what files matter.
|
||||
|
||||
Read, in order, with a token budget — do not scan the whole tree:
|
||||
|
||||
1. `README.md` (root) + first-page of any `README_*.md` variants.
|
||||
2. `CONTRIBUTING.md` / `AGENTS.md` / `CLAUDE.md` at root if present —
|
||||
these often spell out architecture and entry points.
|
||||
3. `package.json` / `pyproject.toml` scripts + entry points.
|
||||
4. The layout of the top two directory levels (not recursive).
|
||||
5. Key config files: `docker-compose.yml`, `Dockerfile`, `Makefile`,
|
||||
`langgraph.json`, `next.config.*`, `nuxt.config.*`.
|
||||
|
||||
Produce `.mem0-integration/repo-summary.md`:
|
||||
|
||||
# Repo comprehension
|
||||
|
||||
**What this repo does:** <one paragraph in plain English. Who is
|
||||
the end user? What does the app do for them? What LLM / agent
|
||||
behavior is central? Do not list dependencies — describe behavior.>
|
||||
|
||||
**Architecture at a glance:**
|
||||
- Backend: <path(s), framework, primary entry point>
|
||||
- Frontend: <path(s) if any, framework — for context only; no
|
||||
integration here>
|
||||
- Agent loop / orchestration: <LangGraph? custom? none?>
|
||||
- Existing memory/session/state systems: <name them — these are
|
||||
what step 6 Coexistence must preserve>
|
||||
|
||||
**Candidate backend integration surfaces** (ranked, best first):
|
||||
1. `<backend-file>:<line_range>` — <function> — <one-sentence
|
||||
reason this is where write/read could slot in without
|
||||
replacing anything existing>
|
||||
2. ...
|
||||
3. ...
|
||||
|
||||
**Not a fit here:** <list anything the skill considered but ruled
|
||||
out — e.g., "frontend chat component: client-side, excluded by
|
||||
backend-only rule"; "existing memory subsystem X: would require
|
||||
replacement, excluded by additive principle">
|
||||
|
||||
**Sources read:** <list the files actually opened, with line counts,
|
||||
so reviewers can verify coverage.>
|
||||
|
||||
Show the user the rendered summary and ask: *"Is this understanding
|
||||
correct? Which of the candidate surfaces (1, 2, 3 ...) should step 3
|
||||
forward target?"*
|
||||
|
||||
Gate rules:
|
||||
|
||||
- If no backend surface is found → exit code 1. The preconditions
|
||||
should already have caught frontend-only repos; reaching this point
|
||||
means a more subtle miss (e.g., the "backend" is actually just a
|
||||
static build). Do not force a fit.
|
||||
- If every candidate surface would require replacing an existing
|
||||
memory/session system → exit code 1 with the "additive principle"
|
||||
rationale. The user can manually point at a non-conflicting location
|
||||
and re-run.
|
||||
- User corrections update `repo-summary.md` and re-confirm. Max 3
|
||||
rounds; beyond that, exit code 1.
|
||||
|
||||
The user's chosen surface index is baked into `product.json` as
|
||||
`preferred_site` and referenced by steps 5 and 6.
|
||||
|
||||
### 3. Product selection — Platform vs OSS (ask with a recommendation)
|
||||
|
||||
Read the `## Identify the User's Setup` block in
|
||||
`https://docs.mem0.ai/llms.txt` for the Platform-first routing rules, then
|
||||
apply the heuristics below. Ask, but never blank:
|
||||
|
||||
- Other managed-service SDKs present (`@clerk/*`, `stripe`, `@supabase/*`,
|
||||
`openai`, `@upstash/*`, `posthog-*`) — 3+ → recommend **Platform**.
|
||||
- Local-infra signals (`docker-compose.yml` with postgres / redis / qdrant /
|
||||
neo4j, ollama configs, self-hosted auth) — 2+ → recommend **OSS**.
|
||||
- No strong signal → default recommendation: **Platform** (lower integration
|
||||
cost; migration later is supported).
|
||||
|
||||
Example:
|
||||
|
||||
> I see `stripe`, `@clerk/nextjs`, and `@supabase/supabase-js` — managed
|
||||
> services throughout. I recommend **Mem0 Platform** (4-line integration).
|
||||
> Override and use open source?
|
||||
|
||||
Bake the choice into the goal doc in step 5. Do not re-decide later.
|
||||
|
||||
### 4. API key check (env-first, then ask)
|
||||
|
||||
| Track | Key | Where to find |
|
||||
|---|---|---|
|
||||
| Platform | `MEM0_API_KEY` | https://app.mem0.ai |
|
||||
| OSS (default LLM) | `OPENAI_API_KEY` | https://platform.openai.com/api-keys |
|
||||
|
||||
If present in env → continue.
|
||||
If missing → **interactive mode** asks; **CI mode** (`MEM0_INTEGRATE_CI=1`)
|
||||
exits with code 2 and the name of the missing key.
|
||||
|
||||
Never echo key values into `trace.jsonl`. Persist to `.env` only with
|
||||
explicit user consent, and append `.env` to `.gitignore` if not already there.
|
||||
|
||||
If the user is on OSS and wants a non-OpenAI LLM, route them to the
|
||||
`components/llms/*` docs and re-run this step with the chosen provider's key.
|
||||
|
||||
### 5. Goal doc — the hard gate
|
||||
|
||||
Write `.mem0-integration/goal.md` and **require user approval before step 6**.
|
||||
|
||||
Template:
|
||||
|
||||
# Mem0 Integration Goal
|
||||
|
||||
**What gets stored:** <one sentence — user utterances? extracted
|
||||
preferences? a specific domain fact like "dietary restrictions"?>
|
||||
|
||||
**When it gets retrieved:** <one sentence — on each user turn? before a
|
||||
specific tool call? at session start?>
|
||||
|
||||
**Why:** <one sentence — the user-visible behavior change. "Assistant
|
||||
remembers previous orders across sessions," not "we added memory.">
|
||||
|
||||
**Product:** Platform | OSS (locked from step 3, do not change)
|
||||
|
||||
**Delegated skill:** <raw URL of the published skill being used
|
||||
from "Skill delegation rules" above, or "none — custom integration
|
||||
against `skills/mem0`">.
|
||||
|
||||
**Out of scope:** <anything explicitly excluded: "no graph memory,"
|
||||
"no multimodal," "no migration from existing store">
|
||||
|
||||
Rules:
|
||||
|
||||
- User must approve explicitly. If they edit the doc, reload and re-confirm.
|
||||
- `goal.md` is the contract the test suite is written against. Never
|
||||
rewrite it after step 6 starts.
|
||||
- Max 3 rejection rounds. On the 4th, exit with code 3 and the rejection
|
||||
notes — the integration is not well-specified enough to proceed.
|
||||
|
||||
### 6. Integration plan — how and where (hard gate)
|
||||
|
||||
Given `goal.md` is "what and why," this step produces "where and how" and
|
||||
gets explicit user sign-off before any code is written.
|
||||
|
||||
The skill does a **scoped** read of the repo (no wide survey):
|
||||
|
||||
- Grep for the LLM call sites that match the goal (e.g., `openai.chat.`,
|
||||
`anthropic.messages.`, `model.generateContent`, `ChatOpenAI`, `createLLM`).
|
||||
- Grep for the user-identity source (`req.user`, `session.user`, `auth()`,
|
||||
`ctx.userId`, cookies).
|
||||
- Check `package.json` / `pyproject.toml` / `requirements.txt` for
|
||||
conflicts (e.g., existing `mem0ai` at a different version).
|
||||
|
||||
Then write `.mem0-integration/plan.md`:
|
||||
|
||||
# Mem0 Integration Plan
|
||||
|
||||
**Write pattern:** <one sentence — e.g., "After each assistant reply,
|
||||
call client.add([user_msg, assistant_msg], user_id=<source>).">
|
||||
|
||||
**Read pattern:** <one sentence — e.g., "Before building the LLM prompt,
|
||||
call client.search(query=latest_user_msg, user_id=<source>, limit=5)
|
||||
and inject results as a system message.">
|
||||
|
||||
**User identifier source:** <code path — e.g., `req.auth.userId`,
|
||||
`session.user.email`, `ctx.params.user_id`. If none, ask the user.>
|
||||
|
||||
**Session scoping:**
|
||||
- user_id: <source>
|
||||
- agent_id: <static slug | null>
|
||||
- run_id: <source | null>
|
||||
|
||||
**Write call site:** `<file:line_range>` — inside `<function>`
|
||||
**Read call site:** `<file:line_range>` — inside `<function>`
|
||||
|
||||
**Dependencies to add:**
|
||||
- `<package>@<version pinned in frontmatter>`
|
||||
|
||||
**Preserved behavior:** <list the existing repo behaviors that must
|
||||
keep working after this edit — e.g., "existing OpenAI streaming still
|
||||
works," "existing Redis session store still used," "existing tests
|
||||
still pass unchanged.">
|
||||
|
||||
**Coexistence:** <one bullet per existing system the integration sits
|
||||
alongside. Name the files/classes. Example: "The existing
|
||||
`agents/memory/storage.py` MemoryStorage class remains untouched and
|
||||
keeps its LangGraph SummarizationEvent flow. Mem0 is added as a
|
||||
parallel long-term-facts store, in a new file, invoked only when
|
||||
MEM0_ENABLED=1 is set.">
|
||||
|
||||
**Feature flag:** <the exact mechanism and the default. Required.
|
||||
Example: `env MEM0_ENABLED=1`, default unset / off; `config.mem0.enabled`,
|
||||
default false. With the flag in its default state, the repo must
|
||||
behave exactly like `main`.>
|
||||
|
||||
**Sources consulted:** <minimum 2 URLs from "Canonical sources" above
|
||||
that informed this plan. At least one `docs.mem0.ai` URL and one
|
||||
delegated-skill URL. Cite the specific section or heading.>
|
||||
|
||||
**E2E recipe:** <how the verification skill should drive the app
|
||||
end-to-end. Omit only if the repo is a pure library with no runnable
|
||||
entry point — in which case the E2E step will skip with a warning.>
|
||||
|
||||
start: <shell command to launch the app locally,
|
||||
using $PORT for any network port>
|
||||
ready_probe: <one of: url=<URL> status=<code> /
|
||||
log="<substring to wait for>" /
|
||||
sleep=<seconds, last resort>>
|
||||
compose_services: <optional: whitespace-separated service
|
||||
names in docker-compose.yml to start first;
|
||||
use label mem0-e2e: "true" to mark them>
|
||||
write_call: <command that triggers the Mem0 write path
|
||||
exactly once; ≤ 60s runtime>
|
||||
write_async_wait_ms: <milliseconds to wait after write_call for
|
||||
async memory flush; default 0>
|
||||
read_call: <command that triggers the Mem0 read path,
|
||||
typically a fresh session / new request>
|
||||
read_assert: <substring, regex, or jsonpath=<expr>=<value>
|
||||
that MUST appear in read_call's output for
|
||||
the E2E to pass. Derived from goal.md's
|
||||
"What gets stored.">
|
||||
|
||||
**Rejected alternatives:** <briefly, 1–2 bullets — patterns the skill
|
||||
considered but did not pick, and why. Helps the user decide.>
|
||||
|
||||
Rules:
|
||||
|
||||
- Show the user the proposed call sites with 10 lines of context around
|
||||
each before asking for approval.
|
||||
- If the skill can't find a plausible call site for either write or read,
|
||||
it exits with code 5 and asks the user to name the file(s) manually
|
||||
(this is the "no fit here" signal — don't guess).
|
||||
- Max 3 rejection rounds on the plan. On the 4th, exit code 5 with the
|
||||
last plan and the user's notes.
|
||||
- If the user edits `plan.md` by hand, reload and re-confirm.
|
||||
|
||||
`plan.md` (not `goal.md`) is the contract the subagent implements against
|
||||
in step 8.
|
||||
|
||||
### 7. Tests first (TDD)
|
||||
|
||||
Main agent writes failing tests against `goal.md` in the repo's native
|
||||
test framework:
|
||||
|
||||
| Track | Default framework |
|
||||
|---|---|
|
||||
| Python | `pytest` |
|
||||
| TypeScript | `vitest` if detected, else `jest` |
|
||||
| JavaScript | same |
|
||||
|
||||
Test assertion shapes must match the **canonical signatures**:
|
||||
|
||||
- Platform method signatures: `https://docs.mem0.ai/openapi.json`
|
||||
(request body schemas for `/v1/memories/` and `/v1/memories/search/`).
|
||||
- OSS method signatures: the delegated skill named in `plan.md`
|
||||
(fetched from its raw URL) or `skills/mem0/SKILL.md` as the default.
|
||||
- Do not hand-roll request shapes. If the delegated skill has an
|
||||
example block, lift it verbatim.
|
||||
|
||||
Minimum two test files (paths taken from `plan.md` call sites):
|
||||
|
||||
- `test_mem0_write.<ext>` — asserts `add()` is called at the Write call
|
||||
site with the right payload shape (Platform messages-array vs OSS string)
|
||||
and the right `user_id` source.
|
||||
- `test_mem0_read.<ext>` — asserts `search()` runs before the Read call
|
||||
site and the result is wired into the LLM prompt / response path.
|
||||
|
||||
Tests MUST be importable with `MEM0_API_KEY` unset. This is the design
|
||||
pressure that forces step 8's lazy `MemoryClient()` / `Memory()`
|
||||
construction — eager module-level init hits the API on import and
|
||||
breaks pre-existing test collection when the key is missing.
|
||||
|
||||
Run the tests. They **must fail**. If they pass before any implementation,
|
||||
the tests are wrong — rewrite them.
|
||||
|
||||
### 8. Implementation (subagent, fresh context)
|
||||
|
||||
Spawn a subagent with:
|
||||
|
||||
- **Inputs**: the repo, `goal.md`, `plan.md`, the two test files, and
|
||||
direct URLs to: the delegated skill (from `plan.md`), the SDK source
|
||||
(pinned per `mem0_tested_versions`), `https://docs.mem0.ai/llms.txt`,
|
||||
and `https://docs.mem0.ai/openapi.json`.
|
||||
- **No access** to main agent's reasoning trace or scratchpad.
|
||||
- **System prompt** (verbatim):
|
||||
|
||||
You are implementing a Mem0 integration for an existing repo.
|
||||
|
||||
Read these first:
|
||||
- plan.md (the mechanical contract)
|
||||
- goal.md (the intent — do not change it)
|
||||
- the test files (do not change them either)
|
||||
- <delegated skill raw URL from plan.md>
|
||||
- https://docs.mem0.ai/llms.txt
|
||||
- https://docs.mem0.ai/openapi.json (Platform only)
|
||||
|
||||
Constraints — all required, all enforced at review:
|
||||
|
||||
1. Touch only the files named in plan.md's call sites, or add
|
||||
strictly new files.
|
||||
2. Do not remove or rename any existing symbol. Do not change
|
||||
any public signature.
|
||||
3. Do not modify any existing test.
|
||||
4. Gate every line of new Mem0 code behind the feature flag from
|
||||
plan.md. With the flag in its default state, the repo must
|
||||
behave exactly like `main` — byte-for-byte, including stdout
|
||||
and return values.
|
||||
5. Use only the <Platform | OSS> SDK surface. No new dependencies
|
||||
beyond those listed under plan.md's "Dependencies to add."
|
||||
6. Preserve everything listed under plan.md's "Preserved behavior"
|
||||
and "Coexistence."
|
||||
7. Lazy client construction. `MemoryClient()` validates the API
|
||||
key in `__init__` (it makes a network call). Never instantiate
|
||||
it at module-import time — construct on first use inside the
|
||||
request / handler path. The same rule applies to OSS `Memory()`,
|
||||
which can eagerly initialize embedding and LLM providers. Use
|
||||
a function-local singleton (`functools.lru_cache`, a module-level
|
||||
`_client = None` + getter, or DI scope) — never a top-level
|
||||
global. Eager init breaks the pre-existing test suite at
|
||||
collection time whenever the key is missing or invalid, which
|
||||
is a non-invasiveness violation.
|
||||
|
||||
Implement the plan to make the new tests pass while all
|
||||
pre-existing tests continue to pass unchanged.
|
||||
|
||||
Subagent returns a diff. Main agent reviews against `plan.md` (the
|
||||
mechanical contract) and `goal.md` (the intent):
|
||||
|
||||
- Approved → apply the diff, commit.
|
||||
- Rejected → return with specific, actionable feedback (not "try again").
|
||||
- Max 3 review loops. Beyond that → exit code 4 with the last diff and
|
||||
reviewer feedback.
|
||||
|
||||
### 9. Commit + handoff
|
||||
|
||||
Create branch `mem0-integrate/<short-goal-slug>` and commit in
|
||||
**separate commits** so reviewers can cherry-pick:
|
||||
|
||||
1. `mem0: add gated dependency` — just the `pyproject.toml` / `package.json`
|
||||
change.
|
||||
2. `mem0: add integration module` — the new file(s).
|
||||
3. `mem0: wire into <call site>` — the call-site edit(s), still gated.
|
||||
4. `mem0: add tests` — the new test files.
|
||||
|
||||
If `--no-heal` is set → print `Run /mem0-test-integration to verify.`
|
||||
and exit. Otherwise proceed to step 10.
|
||||
|
||||
### 10. Self-healing loop (default ON; disable with `--no-heal`)
|
||||
|
||||
Run `/mem0-test-integration --ci` in a subprocess. If `scorecard.json`
|
||||
reports `overall: pass` → done, exit 0.
|
||||
|
||||
Otherwise loop:
|
||||
|
||||
1. **Categorize the failing check** from `scorecard.json`. Route per
|
||||
category:
|
||||
- `install` / `static_checks` → dependency or import fix.
|
||||
- `unit_tests` → wiring or assertion fix.
|
||||
- `smoke_test` → API key or SDK call-shape fix.
|
||||
- `e2e_test` → recipe, flag-wiring, or integration-point fix.
|
||||
- **Pre-existing test failure (test skill exit code 7,
|
||||
`non_invasive: false` in scorecard) → STOP.** This is a
|
||||
non-invasiveness violation. Do NOT attempt to "fix" it (that
|
||||
breaks principle 3). Exit code 6 with rationale.
|
||||
|
||||
2. **Spawn a remediation subagent**, fresh context. Inputs:
|
||||
`plan.md`, `goal.md`, `scorecard.md`, `scorecard.json`, the last
|
||||
committed diff, and the relevant log file for the failing category
|
||||
(`test-stdout.log` / `smoke-stdout.log` / `e2e-app.log` /
|
||||
`e2e-calls.log`).
|
||||
|
||||
System prompt (verbatim):
|
||||
|
||||
You are fixing a failing Mem0 integration test.
|
||||
|
||||
Non-negotiable constraints:
|
||||
- Do not modify test files.
|
||||
- Do not remove or rename any existing symbol or signature.
|
||||
- Do not change pre-existing behavior. The feature flag from
|
||||
plan.md must still default to OFF, and with the flag in its
|
||||
default state the repo must behave exactly like main.
|
||||
- Touch only the files named in plan.md's call sites, or add
|
||||
strictly new files.
|
||||
- Return the smallest possible diff that fixes the single
|
||||
failing check listed in scorecard.md. No drive-by cleanup.
|
||||
|
||||
3. **Apply the diff**; commit on the same branch with message
|
||||
`mem0-heal: <category> attempt <N>`. Do NOT amend earlier commits
|
||||
(reviewers need the heal trail).
|
||||
|
||||
4. **Re-run `/mem0-test-integration --ci`**. Outcomes:
|
||||
- `overall: pass` → done, exit 0.
|
||||
- Same check still failing → increment attempt counter; loop.
|
||||
- A *different* check now failing → regression. Revert the heal
|
||||
commit (`git revert HEAD --no-edit`), record the regression in
|
||||
`.mem0-integration/heal-trace.md`, exit code 6.
|
||||
|
||||
5. **Bounded iterations.** Default 3 attempts per failing category.
|
||||
Override with `--heal-max N` (hard cap 10). On exhaustion, exit 6
|
||||
with the full attempt trace: each diff, each scorecard, final log
|
||||
tail.
|
||||
|
||||
6. **Post-loop summary** written to `.mem0-integration/heal-trace.md`:
|
||||
which category failed, how many attempts, each diff's intent, final
|
||||
status, and — on success — the delta from initial scorecard to final.
|
||||
|
||||
## Artifacts (all under `.mem0-integration/`)
|
||||
|
||||
| File | Purpose | Retention |
|
||||
|---|---|---|
|
||||
| `repo-summary.md` | Repo comprehension + candidate backend surfaces (step 2). | Keep across runs. |
|
||||
| `goal.md` | Approved intent. Never rewritten after step 6. | Keep across runs. |
|
||||
| `plan.md` | Approved mechanics (where, how, call sites, preserved behavior). | Keep across runs. |
|
||||
| `trace.jsonl` | Every tool call, decision, and subagent exchange this run. | Overwritten per run. |
|
||||
| `diff.patch` | The committed integration as a reviewable patch. | Overwritten per run. |
|
||||
| `heal-trace.md` | Per-attempt record of the self-healing loop (step 10). | Overwritten per run. |
|
||||
| `product.json` | `{"product": "platform"\|"oss", "language": "...", "mem0_version": "...", "write_site": "file:line", "read_site": "file:line", "feature_flag": "MEM0_ENABLED"}` — consumed by the verification skill. | Overwritten per run. |
|
||||
|
||||
`.mem0-integration/` is added to `.gitignore` on first run. Nothing is
|
||||
written outside this directory and the repo's source tree.
|
||||
|
||||
## Modes
|
||||
|
||||
| Mode | Trigger | Behavior |
|
||||
|---|---|---|
|
||||
| Interactive (default) | TTY present, `MEM0_INTEGRATE_CI` unset | Asks for keys, confirms goal doc, shows recommendations. |
|
||||
| CI | `MEM0_INTEGRATE_CI=1` | Requires keys in env, requires `--product`, auto-approves goal doc from `goal.md` if present, fails fast otherwise. |
|
||||
|
||||
## Invocation
|
||||
|
||||
/mem0-integrate # interactive, heal ON
|
||||
/mem0-integrate --no-heal # stop after commit; manual verify
|
||||
/mem0-integrate --heal-max 5 # cap heal attempts per category (default 3)
|
||||
/mem0-integrate --product platform # skip the product ask
|
||||
/mem0-integrate --product oss
|
||||
/mem0-integrate --ci # non-interactive (for test harness)
|
||||
|
||||
## Exit codes
|
||||
|
||||
| Code | Meaning |
|
||||
|---|---|
|
||||
| 0 | Success. Feature branch committed; verification skill ready to run. |
|
||||
| 1 | Precondition failed (dirty repo, no detectable language, etc.). |
|
||||
| 2 | Missing env key in CI mode. |
|
||||
| 3 | Goal doc rejected 3+ times — integration is not well-specified. |
|
||||
| 4 | Subagent review loop did not converge in 3 rounds. |
|
||||
| 5 | Integration plan rejected 3+ times, or no plausible additive call site found. |
|
||||
| 6 | Self-healing loop did not converge, detected a non-invasiveness violation, or a pre-existing test failed. |
|
||||
|
||||
## Explicitly out of scope
|
||||
|
||||
- Surveying the repo for fit points. Humans decide where Mem0 helps before
|
||||
invoking this skill.
|
||||
- Replacing any existing memory / session / state system. Always additive
|
||||
and feature-flagged; see "Integration principles."
|
||||
- Modifying pre-existing tests, even to "fix" them under self-heal. Tests
|
||||
that fail after integration with the flag unset are a non-invasiveness
|
||||
violation, not a bug to patch.
|
||||
- Deciding Platform vs OSS silently. Always ask with a recommendation.
|
||||
- Switching branches, pushing, or opening PRs. Commits locally and stops
|
||||
(or enters the heal loop, still local).
|
||||
- Data migration between stores. Point user at `migration/oss-to-platform`
|
||||
docs if they ask.
|
||||
- Provider selection beyond the default LLM for OSS. If they need a custom
|
||||
LLM / embedder / vector store, route to `components/*` docs and re-run
|
||||
step 4 with the new key.
|
||||
@@ -1,189 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but not
|
||||
limited to compiled object code, generated documentation, and
|
||||
conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work.
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by the Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding any notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2024 Mem0.ai
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,81 +0,0 @@
|
||||
# mem0-test-integration — Pipeline Skill
|
||||
|
||||
Verify a Mem0 integration produced by [`/mem0-integrate`](../mem0-integrate/SKILL.md). Runs in the same workspace on the same branch — installs dependencies, runs the repo's native test suite, then exercises a real end-to-end smoke flow against the user's API key.
|
||||
|
||||
> **This is a pipeline skill, not a reference skill.** Invoke it as `/mem0-test-integration` after `/mem0-integrate` has produced a branch to verify. It catches compile and runtime bugs by design — logical integration errors (wrong data stored, wrong scoping) are for human review.
|
||||
>
|
||||
> **Part of the Mem0 Skill Graph:**
|
||||
> - Reference: [mem0](../mem0/SKILL.md) · [mem0-cli](../mem0-cli/SKILL.md) · [mem0-vercel-ai-sdk](../mem0-vercel-ai-sdk/SKILL.md)
|
||||
> - Pipeline: [mem0-integrate](../mem0-integrate/SKILL.md) → **mem0-test-integration** (this skill)
|
||||
|
||||
## What This Skill Does
|
||||
|
||||
When invoked, your assistant will:
|
||||
|
||||
- **Refuse to start** unless the branch has `.mem0-integration/` artifacts, the working tree is clean, and the right API key is in the environment
|
||||
- **Install** the repo's dependencies using its native tooling (pip, pnpm, npm, hatch, etc.)
|
||||
- **Run the native test suite** in two passes: flag-unset (must behave like `main`) and flag-set (new tests run)
|
||||
- **Execute a real end-to-end smoke flow** against Mem0 Platform (`MEM0_API_KEY`) or OSS (`OPENAI_API_KEY`)
|
||||
- **Produce a scorecard** — `overall: pass | fail`, per-check reasons, and the reproduction command for each failure
|
||||
|
||||
## When to Use
|
||||
|
||||
Trigger phrases:
|
||||
|
||||
- "Verify the integration"
|
||||
- "Test the Mem0 integration"
|
||||
- "Run `/mem0-test-integration`"
|
||||
|
||||
Do **not** use this skill to run general project tests (defer to the repo's native test command) or before `/mem0-integrate` has produced a branch on the current workspace.
|
||||
|
||||
## Installation
|
||||
|
||||
### CLI (Claude Code, Codex, OpenCode, OpenClaw, or any tool that supports skills)
|
||||
|
||||
```bash
|
||||
npx skills add https://github.com/mem0ai/mem0 --skill mem0-test-integration
|
||||
```
|
||||
|
||||
Typically installed alongside the companion pipeline skill:
|
||||
|
||||
```bash
|
||||
npx skills add https://github.com/mem0ai/mem0 --skill mem0-integrate
|
||||
```
|
||||
|
||||
### Claude.ai
|
||||
|
||||
1. Download this `skills/mem0-test-integration` folder as a ZIP
|
||||
2. Go to **Settings > Capabilities > Skills**
|
||||
3. Click **Upload skill** and select the ZIP
|
||||
|
||||
### Claude API (Skills API)
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.anthropic.com/v1/skills \
|
||||
-H "x-api-key: $ANTHROPIC_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name": "mem0-test-integration", "source": "https://github.com/mem0ai/mem0/tree/main/skills/mem0-test-integration"}'
|
||||
```
|
||||
|
||||
### Preconditions
|
||||
|
||||
The skill refuses to start unless all of the following are true:
|
||||
|
||||
- `.mem0-integration/` directory exists in the repo root
|
||||
- Current branch starts with `mem0-integrate/`
|
||||
- Working tree is clean
|
||||
- The same API key used during `/mem0-integrate` is exported in the environment
|
||||
|
||||
## What This Skill Does *Not* Catch
|
||||
|
||||
By design, this skill only catches compile and runtime bugs. Logical errors — memories stored with the wrong scoping, retrieval returning the wrong user's data, filter mismatches — are the human reviewer's responsibility.
|
||||
|
||||
## Links
|
||||
|
||||
- [Mem0 Documentation](https://docs.mem0.ai)
|
||||
- [Mem0 GitHub](https://github.com/mem0ai/mem0)
|
||||
- [API Reference](https://docs.mem0.ai/api-reference)
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -1,368 +0,0 @@
|
||||
---
|
||||
name: mem0-test-integration
|
||||
description: >
|
||||
Verify a Mem0 integration produced by /mem0-integrate. Runs in the same
|
||||
workspace on the same branch (loose coupling) — installs dependencies,
|
||||
runs the repo's native test suite, then exercises a real end-to-end
|
||||
smoke flow against the user's API key. Produces a scorecard.
|
||||
TRIGGER when: user has just run /mem0-integrate and says "verify",
|
||||
"test the integration", "run /mem0-test-integration", or when a
|
||||
.mem0-integration/ directory exists and tests have not been run yet
|
||||
on the current branch.
|
||||
DO NOT TRIGGER when: the user wants to run general project tests
|
||||
(defer to the repo's native test command), or when no prior /mem0-integrate
|
||||
run exists in the current branch (ask them to run /mem0-integrate first).
|
||||
This skill ONLY catches compile and runtime bugs by design. Logical
|
||||
integration errors — wrong data stored, wrong time retrieved, wrong
|
||||
user scoping — are on the human reviewer.
|
||||
license: Apache-2.0
|
||||
metadata:
|
||||
author: mem0ai
|
||||
version: "0.1.0"
|
||||
category: ai-memory
|
||||
tags: "memory, integration, testing, tdd, platform, oss"
|
||||
coupling: loose
|
||||
mem0_tested_versions: "mem0ai (PyPI) >=2.0.0,<3.0.0; mem0ai (npm) >=3.0.0,<4.0.0"
|
||||
---
|
||||
|
||||
# mem0-test-integration
|
||||
|
||||
Verifies what `/mem0-integrate` produced. Runs in the same workspace,
|
||||
on the same feature branch. Loose coupling — fast, catches compile and
|
||||
runtime bugs, does not catch logical errors.
|
||||
|
||||
## Canonical sources (use these, not ambient knowledge)
|
||||
|
||||
All static checks and smoke-test shapes validate against these URLs.
|
||||
`WebFetch` each before running step 3.
|
||||
|
||||
- Scope-tagged docs index: https://docs.mem0.ai/llms.txt
|
||||
- OpenAPI (Platform REST): https://docs.mem0.ai/openapi.json
|
||||
- Published SDK skill (canonical call patterns): https://raw.githubusercontent.com/mem0ai/mem0/main/skills/mem0/SKILL.md
|
||||
- Vercel AI SDK skill (if the target repo uses `@ai-sdk/*`): https://raw.githubusercontent.com/mem0ai/mem0/main/skills/mem0-vercel-ai-sdk/SKILL.md
|
||||
- SDK source (cross-check version against frontmatter `mem0_tested_versions`):
|
||||
- Repo root: https://github.com/mem0ai/mem0
|
||||
- Python: https://github.com/mem0ai/mem0/tree/main/mem0
|
||||
- TypeScript: https://github.com/mem0ai/mem0/tree/main/mem0-ts
|
||||
|
||||
Read the `Delegated skill:` field in `.mem0-integration/plan.md` — if it
|
||||
names a skill URL, fetch that skill and use its example blocks as the
|
||||
reference for both static checks (step 3) and the smoke test (step 5).
|
||||
|
||||
## Non-invasiveness contract
|
||||
|
||||
Every check in this skill assumes the integration is **additive and
|
||||
feature-flagged** (see `/mem0-integrate` "Integration principles").
|
||||
Specifically:
|
||||
|
||||
- `product.json` must contain a `feature_flag` field.
|
||||
- Steps 4–6 run in two passes:
|
||||
- **Pass A — flag unset.** All pre-existing tests must pass, smoke/E2E
|
||||
skip. The repo must behave like `main`. Any failure here is a
|
||||
**hard fail** — do not let the self-heal loop attempt a patch.
|
||||
- **Pass B — flag set.** New tests must pass, smoke and E2E run.
|
||||
- If Pass A fails, the scorecard marks `non_invasive: false` and sets
|
||||
`overall: fail` with a distinct reason code the integrator's heal
|
||||
loop refuses to touch.
|
||||
|
||||
## Preconditions
|
||||
|
||||
Refuse to start unless ALL of the following are true:
|
||||
|
||||
- `.mem0-integration/` directory exists in the repo root.
|
||||
- `.mem0-integration/product.json`, `goal.md`, and `plan.md` are readable
|
||||
and internally consistent (JSON parses, docs non-empty).
|
||||
- Current branch name begins with `mem0-integrate/` (set by the companion
|
||||
skill). Prevents accidental runs on unrelated branches.
|
||||
- Working tree is clean. The skill never modifies source files; any dirty
|
||||
state means the integration is mid-edit and not ready to verify.
|
||||
- The same API key the integration used is available in the environment
|
||||
(`MEM0_API_KEY` for Platform, `OPENAI_API_KEY` for OSS — read which from
|
||||
`product.json`). Interactive mode asks if missing; CI mode exits 2.
|
||||
|
||||
Exit with a written rationale on any precondition failure. Never attempt
|
||||
to "fix up" state.
|
||||
|
||||
## Pipeline
|
||||
|
||||
### 1. Read the contract
|
||||
|
||||
Load:
|
||||
|
||||
- `product.json` → which language, which product (Platform vs OSS), which
|
||||
mem0 version, `write_site`, `read_site`.
|
||||
- `plan.md` → the mechanical contract (write pattern, read pattern,
|
||||
preserved behavior).
|
||||
- `goal.md` → the intent (displayed in the scorecard only; not tested).
|
||||
|
||||
### 2. Install dependencies
|
||||
|
||||
Route by language from `product.json`:
|
||||
|
||||
| Language | Command |
|
||||
|---|---|
|
||||
| Python | `pip install -e .` if editable, else `pip install -r requirements.txt`. Then `pip install mem0ai` if not already present at the pinned version. |
|
||||
| TypeScript / JavaScript | `npm install` (or `pnpm install` / `yarn install` if detected by lockfile). |
|
||||
|
||||
If install fails → exit code 2 with stderr tail. Never move to testing
|
||||
if dependencies don't resolve.
|
||||
|
||||
### 3. Static sanity checks (fast, local, no API calls)
|
||||
|
||||
- **Import check**: does the write-site file import the expected Mem0
|
||||
surface? Authoritative list comes from `## Identify the User's Setup`
|
||||
in `https://docs.mem0.ai/llms.txt`:
|
||||
- Platform Python → `from mem0 import MemoryClient`
|
||||
- Platform TS → `import MemoryClient from "mem0ai"`
|
||||
- OSS Python → `from mem0 import Memory`
|
||||
- OSS TS → `import { Memory } from "mem0ai/oss"`
|
||||
|
||||
If `plan.md` names a delegated skill (e.g., Vercel AI), use *that*
|
||||
skill's import signature instead of the list above. Mismatch → fail
|
||||
with line number.
|
||||
- **Version check**: installed `mem0ai` version falls in the range from
|
||||
this skill's `mem0_tested_versions`. Out of range → warn but continue.
|
||||
- **Type check** (TS tracks only): run `tsc --noEmit` or `tsup --dts`.
|
||||
Non-zero → fail.
|
||||
- **Lint** (if the repo has a linter configured): run the repo's own
|
||||
lint command. Lint failures from this skill's changes → fail; pre-existing
|
||||
lint failures → surface as a warning.
|
||||
- **Eager-init check**: grep the `write_site` and `read_site` files (paths
|
||||
from `product.json`) for `MemoryClient(` or `Memory(` at module scope —
|
||||
i.e., not inside a function, method, or class body. `MemoryClient()`
|
||||
validates the API key in `__init__` (network call) and OSS `Memory()`
|
||||
can eagerly initialize embedding/LLM providers — module-level
|
||||
instantiation hits the wire on import and breaks Pass A's test
|
||||
collection whenever the key is unset. Hit → fail with `file:line` and
|
||||
the lazy-init guidance from `/mem0-integrate` step 8 constraint #7.
|
||||
|
||||
### 4. Run the repo's native test suite (two passes)
|
||||
|
||||
| Language | Test command (in priority order) |
|
||||
|---|---|
|
||||
| Python | `pytest` with the test files from step 5 of the companion skill, else `python -m unittest discover`. |
|
||||
| TypeScript / JavaScript | `npm test` if defined in package.json; else auto-detect `vitest` or `jest`. |
|
||||
|
||||
**Pass A — `feature_flag` unset.** Run the *entire* pre-existing suite
|
||||
(excluding the new `test_mem0_*` files). **Must be 100% green.** Any
|
||||
failure here marks `non_invasive: false` in the scorecard and is
|
||||
a **hard fail** — the integrator's self-heal loop refuses to touch it.
|
||||
|
||||
**Pass B — `feature_flag` set** (value from `product.json`). Run the
|
||||
full suite including the new tests. All must pass.
|
||||
|
||||
Isolate integration-introduced failures using `git diff main..HEAD
|
||||
--name-only`. A test file that exists on `main` and fails only under
|
||||
the integration branch (flag set *or* unset) counts against the
|
||||
scorecard regardless of pass. A test file that already failed on `main`
|
||||
is surfaced as `pre_existing_unrelated` and does not count — but is
|
||||
still reported so the user can clean it up.
|
||||
|
||||
Capture output to `.mem0-integration/test-stdout-flag-off.log` and
|
||||
`.mem0-integration/test-stdout-flag-on.log`. Scorecard reports pass/fail
|
||||
per pass.
|
||||
|
||||
### 5. Smoke test (real API call, shortest round-trip)
|
||||
|
||||
Scripted end-to-end flow tailored to `product.json`. The call shapes
|
||||
below are the minimal ones; if `plan.md` names a delegated skill, use
|
||||
*that skill's* minimal example verbatim instead — it is the canonical
|
||||
shape for the detected stack.
|
||||
|
||||
**Platform (Python):**
|
||||
|
||||
from mem0 import MemoryClient
|
||||
c = MemoryClient() # uses MEM0_API_KEY
|
||||
uid = f"mem0-test-integration-{os.urandom(4).hex()}"
|
||||
c.add([{"role": "user", "content": "I prefer aisle seats"}], user_id=uid)
|
||||
hits = c.search("seat preference", user_id=uid)
|
||||
assert any("aisle" in h.get("memory", "") for h in hits), hits
|
||||
c.delete_all(user_id=uid) # clean up
|
||||
|
||||
**Platform (TS):** same shape with `MemoryClient` from `"mem0ai"`.
|
||||
|
||||
**OSS (Python / TS):** uses `Memory()` / `new Memory()` with default config
|
||||
(OpenAI LLM via `OPENAI_API_KEY`, local Qdrant). If the repo ships a
|
||||
`docker-compose.yml` with a Qdrant service, the skill starts it first and
|
||||
tears it down after. If no backing store is reachable → fail with a
|
||||
clear message naming the fix.
|
||||
|
||||
The smoke test always uses a **disposable random user_id** prefixed with
|
||||
`mem0-test-integration-` so a failed cleanup doesn't pollute the user's
|
||||
real data. A background tidy step deletes any prefix-matching entries
|
||||
older than 24 hours on the next run.
|
||||
|
||||
Capture output to `.mem0-integration/smoke-stdout.log`.
|
||||
|
||||
### 6. E2E integration test (run the app, exercise the flow)
|
||||
|
||||
Unit tests + smoke prove the SDK works in isolation. This step is the
|
||||
real signal: **does memory actually appear in the app's user-visible
|
||||
output when the integration runs end-to-end?**
|
||||
|
||||
Requires `plan.md` to contain an `E2E recipe:` section (authored by
|
||||
`/mem0-integrate` step 5). If absent → status `skipped` (not `fail`),
|
||||
note in scorecard that the repo has no runnable entry point.
|
||||
|
||||
Recipe fields the skill reads:
|
||||
|
||||
- `start` — shell command to launch the app using `$PORT` for any network
|
||||
port. Run in background with stdout/stderr teed to
|
||||
`.mem0-integration/e2e-app.log`.
|
||||
- `ready_probe` — how to detect readiness. `url=... status=...` polls an
|
||||
HTTP endpoint; `log="..."` waits for a substring in `e2e-app.log`;
|
||||
`sleep=N` waits N seconds (last resort). 60-second hard timeout.
|
||||
- `compose_services` — optional. If set, bring them up via
|
||||
`docker compose up -d <services>` before `start`, tear them down with
|
||||
`docker compose down` at the end.
|
||||
- `write_call` — triggers the Mem0 write path exactly once. Output is
|
||||
captured and surfaced on failure. 60-second hard timeout.
|
||||
- `write_async_wait_ms` — pause after `write_call` to let async memory
|
||||
flushes land. Default 0.
|
||||
- `read_call` — triggers the Mem0 read path. Typically a fresh session
|
||||
or new request that should surface the stored memory.
|
||||
- `read_assert` — substring, `regex=...`, or `jsonpath=<expr>=<value>`
|
||||
that must appear in `read_call`'s stdout. This is the E2E pass gate.
|
||||
|
||||
Execution order:
|
||||
|
||||
1. Allocate an ephemeral TCP port; export as `PORT`.
|
||||
2. Set `MEM0_USER_ID` to a disposable `mem0-test-integration-<rand>` value
|
||||
and export it, so the app can use the same scoping the smoke test does
|
||||
if the recipe wants cleanup.
|
||||
3. Bring up `compose_services` if named.
|
||||
4. Run `start` in the background.
|
||||
5. Poll `ready_probe` until success or 60s timeout. Timeout → fail.
|
||||
6. Run `write_call`. Non-zero exit → fail (but continue to cleanup).
|
||||
7. Sleep `write_async_wait_ms`.
|
||||
8. Run `read_call`.
|
||||
9. Evaluate `read_assert` against `read_call`'s stdout. Miss → fail.
|
||||
10. Cleanup (always, even on failure): SIGTERM the app, SIGKILL after
|
||||
5s, `docker compose down` if services were started, `delete_all`
|
||||
memories matching `mem0-test-integration-*` on Platform scenarios.
|
||||
|
||||
On any failure, the scorecard includes:
|
||||
|
||||
- Last 40 lines of `e2e-app.log`
|
||||
- Full `write_call` output
|
||||
- Full `read_call` output
|
||||
- The expected vs actual for `read_assert`
|
||||
|
||||
### 7. Scorecard
|
||||
|
||||
Write `.mem0-integration/scorecard.md` and `.mem0-integration/scorecard.json`:
|
||||
|
||||
{
|
||||
"timestamp": "2026-04-20T14:03:11Z",
|
||||
"branch": "mem0-integrate/remember-user-preferences",
|
||||
"product": "platform",
|
||||
"language": "python",
|
||||
"mem0_version": "2.0.0",
|
||||
"non_invasive": true,
|
||||
"feature_flag": "MEM0_ENABLED",
|
||||
"results": {
|
||||
"install": {"status": "pass", "duration_ms": 12043},
|
||||
"static_checks":{"status": "pass", "duration_ms": 812},
|
||||
"unit_tests_flag_off": {"status": "pass", "duration_ms": 3920, "count": 47,
|
||||
"reason": "all pre-existing tests green with flag unset"},
|
||||
"unit_tests_flag_on": {"status": "pass", "duration_ms": 4321, "count": 49},
|
||||
"smoke_test": {"status": "pass", "duration_ms": 2890, "memory_id": "mem_..."},
|
||||
"e2e_test": {"status": "pass", "duration_ms": 14200,
|
||||
"ready_probe_ms": 3100, "write_exit": 0,
|
||||
"read_assert_matched": true}
|
||||
},
|
||||
"friction": {
|
||||
"dependency_install_retries": 0,
|
||||
"pre_existing_test_failures": 0,
|
||||
"warnings": ["mem0ai 2.0.0 pinned; consider 2.0.1 for fix X"]
|
||||
},
|
||||
"overall": "pass"
|
||||
}
|
||||
|
||||
The markdown version is human-readable and includes:
|
||||
|
||||
- Goal doc + plan doc reprinted at top (so reviewers don't have to hunt).
|
||||
- Each check with pass/fail + log excerpt.
|
||||
- Friction summary.
|
||||
- Verbatim warnings from mem0 SDK (if any — e.g., deprecated field usage).
|
||||
- **Explicit "NOT checked" section** listing what loose coupling misses:
|
||||
"Whether the stored data is what the user wants stored. Whether search
|
||||
runs at the right moment. Whether user_id matches the actual session
|
||||
scope. Human review required."
|
||||
|
||||
### 8. Report + exit
|
||||
|
||||
- Print the scorecard path + overall pass/fail to stdout.
|
||||
- **Do not commit the scorecard files.** They live in `.mem0-integration/`,
|
||||
which is gitignored. The user can inspect and optionally pin.
|
||||
- On fail: print the first failing step's log tail (last 40 lines) and
|
||||
stop. Do not attempt to fix anything.
|
||||
|
||||
## Artifacts (all under `.mem0-integration/`)
|
||||
|
||||
| File | Purpose | Retention |
|
||||
|---|---|---|
|
||||
| `scorecard.md` | Human-readable verdict. | Overwritten per run. |
|
||||
| `scorecard.json` | Machine-readable verdict. Consumed by the CI scorecard workflow later. | Overwritten per run. |
|
||||
| `test-stdout-flag-off.log` | Step 4 Pass A (pre-existing suite, flag unset). | Overwritten per run. |
|
||||
| `test-stdout-flag-on.log` | Step 4 Pass B (full suite, flag set). | Overwritten per run. |
|
||||
| `smoke-stdout.log` | Full output from step 5. | Overwritten per run. |
|
||||
| `e2e-app.log` | Background app stdout/stderr from step 6. | Overwritten per run. |
|
||||
| `e2e-calls.log` | write_call + read_call invocations and outputs. | Overwritten per run. |
|
||||
|
||||
## Modes
|
||||
|
||||
| Mode | Trigger | Behavior |
|
||||
|---|---|---|
|
||||
| Interactive (default) | TTY present, `MEM0_TEST_CI` unset | Asks for missing keys, prints friendly summaries. |
|
||||
| CI | `MEM0_TEST_CI=1` | Keys must be in env, no prompts, non-zero exit on any fail. JSON scorecard goes to stdout's tail for workflow parsing. |
|
||||
|
||||
## Invocation
|
||||
|
||||
/mem0-test-integration # interactive, all steps
|
||||
/mem0-test-integration --ci # non-interactive
|
||||
/mem0-test-integration --skip-smoke # no API calls, no E2E
|
||||
/mem0-test-integration --skip-e2e # unit + smoke only (faster CI)
|
||||
/mem0-test-integration --only-smoke # just smoke
|
||||
/mem0-test-integration --only-e2e # just E2E (assumes deps installed)
|
||||
|
||||
Composition: `--skip-*` can stack (`--skip-smoke --skip-e2e` = static +
|
||||
unit only, zero API cost). `--only-*` is mutually exclusive with all
|
||||
other flags.
|
||||
|
||||
## Exit codes
|
||||
|
||||
| Code | Meaning |
|
||||
|---|---|
|
||||
| 0 | All checks passed. |
|
||||
| 1 | Precondition failed (no `.mem0-integration/`, wrong branch, dirty tree). |
|
||||
| 2 | Missing env key (CI mode) or dependency install failure. |
|
||||
| 3 | Static sanity check failed (wrong import, type error). |
|
||||
| 4 | Unit tests failed (Pass B — integration itself broken). |
|
||||
| 5 | Smoke test failed. |
|
||||
| 6 | E2E test failed (ready_probe timeout, write/read call failed, or read_assert miss). |
|
||||
| 7 | Non-invasiveness violation: Pass A failed (pre-existing tests broke). Integrator's heal loop refuses to touch this. |
|
||||
| 8 | Internal error (skill bug — report it). |
|
||||
|
||||
## Explicitly out of scope
|
||||
|
||||
- **Modifying source files.** The skill is read-only against the repo.
|
||||
If verification exposes a bug, re-run `/mem0-integrate` on the same
|
||||
goal + plan; do not hand-patch.
|
||||
- **Fixing broken tests.** Failing unit tests are a signal that the
|
||||
integration is wrong, not that the tests are wrong. The skill does
|
||||
not "try a different test."
|
||||
- **Deep logical correctness.** The E2E step proves "something the user
|
||||
said earlier comes back later," which is a useful but shallow signal.
|
||||
It does NOT prove the integration picks the *right* facts to store,
|
||||
scopes `user_id` correctly across real users, or handles conflict
|
||||
resolution well. That's human review territory.
|
||||
- **Self-healing.** This skill never modifies source files. The paired
|
||||
`/mem0-integrate` skill in its default `--heal` mode consumes the
|
||||
scorecard produced here and drives its own remediation loop. Exit
|
||||
code 7 (non-invasiveness violation) is the explicit signal the heal
|
||||
loop must stop and surface to the user.
|
||||
- **Cross-branch comparisons.** No `main` baseline diffing. The
|
||||
scorecard reflects this branch only.
|
||||
- **Running against production data.** Every smoke test uses a disposable
|
||||
random user_id and cleans up after. Never touches any other user's data.
|
||||
@@ -1,97 +0,0 @@
|
||||
"""Tests for ``mem0.client.project.Project.update`` — focused on the
|
||||
parameter-passthrough surface.
|
||||
|
||||
Verifies the kwarg → JSON payload mapping for every supported field
|
||||
(``custom_instructions``, ``custom_categories``, ``retrieval_criteria``,
|
||||
``multilingual``, ``decay``), the ValueError when no field is
|
||||
provided, and the URL/method shape. The HTTP layer is mocked.
|
||||
"""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def project():
|
||||
"""Build a ``Project`` with a mocked httpx client.
|
||||
|
||||
Bypasses ``MemoryClient`` so the test stays focused on
|
||||
``Project.update`` payload construction.
|
||||
"""
|
||||
http = MagicMock()
|
||||
http.patch.return_value = MagicMock(
|
||||
json=lambda: {"message": "Updated"},
|
||||
raise_for_status=lambda: None,
|
||||
)
|
||||
with patch("mem0.client.project.capture_client_event"):
|
||||
from mem0.client.project import Project
|
||||
|
||||
proj = Project(client=http, org_id="org1", project_id="proj1")
|
||||
yield proj, http
|
||||
|
||||
|
||||
def _patch_payload(http):
|
||||
"""Return the JSON body sent on the last PATCH, stripped of the SDK's
|
||||
standard auth params (``org_id``, ``project_id``) that ``_prepare_params``
|
||||
injects on every request."""
|
||||
assert http.patch.called, "expected a PATCH call"
|
||||
_, kwargs = http.patch.call_args
|
||||
body = dict(kwargs.get("json", {}))
|
||||
body.pop("org_id", None)
|
||||
body.pop("project_id", None)
|
||||
return body
|
||||
|
||||
|
||||
class TestProjectUpdateDecay:
|
||||
def test_decay_true_sent_in_payload(self, project):
|
||||
proj, http = project
|
||||
proj.update(decay=True)
|
||||
assert _patch_payload(http) == {"decay": True}
|
||||
|
||||
def test_decay_false_sent_in_payload(self, project):
|
||||
"""Explicit ``False`` must round-trip — not be filtered as falsy."""
|
||||
proj, http = project
|
||||
proj.update(decay=False)
|
||||
assert _patch_payload(http) == {"decay": False}
|
||||
|
||||
def test_decay_combined_with_multilingual(self, project):
|
||||
proj, http = project
|
||||
proj.update(multilingual=True, decay=True)
|
||||
assert _patch_payload(http) == {
|
||||
"multilingual": True,
|
||||
"decay": True,
|
||||
}
|
||||
|
||||
def test_decay_omitted_when_none(self, project):
|
||||
"""When the caller doesn't pass ``decay``, it must not appear in
|
||||
the payload — backwards compatible with pre-decay callers."""
|
||||
proj, http = project
|
||||
proj.update(multilingual=False)
|
||||
payload = _patch_payload(http)
|
||||
assert payload == {"multilingual": False}
|
||||
assert "decay" not in payload
|
||||
|
||||
def test_no_args_raises_with_decay_in_message(self, project):
|
||||
proj, _ = project
|
||||
with pytest.raises(ValueError, match=r"decay"):
|
||||
proj.update()
|
||||
|
||||
def test_url_targets_project_endpoint(self, project):
|
||||
proj, http = project
|
||||
proj.update(decay=True)
|
||||
args, _ = http.patch.call_args
|
||||
assert args[0] == "/api/v1/orgs/organizations/org1/projects/proj1/"
|
||||
|
||||
|
||||
class TestProjectUpdateBackwardsCompat:
|
||||
def test_multilingual_only_still_works(self, project):
|
||||
"""Pre-decay callers (multilingual only) keep working unchanged."""
|
||||
proj, http = project
|
||||
proj.update(multilingual=True)
|
||||
assert _patch_payload(http) == {"multilingual": True}
|
||||
|
||||
def test_custom_instructions_only_still_works(self, project):
|
||||
proj, http = project
|
||||
proj.update(custom_instructions="be concise")
|
||||
assert _patch_payload(http) == {"custom_instructions": "be concise"}
|
||||
Reference in New Issue
Block a user