From cbfb37c6230a4eb681b6071db7cac66bf5391dd2 Mon Sep 17 00:00:00 2001 From: kartik-mem0 Date: Fri, 25 Sep 2026 10:58:25 +0530 Subject: [PATCH] fix(plugins): give the MCP server the hooks' data dir, release 0.3.4 mcp_server.py never calls configure_harness, so it resolved ~/.mem0/mem0-plugin while the Cursor, Kimi and Antigravity hooks use ~/.mem0/-plugin. A key set in Cursor's plugin settings and cached by the sessionStart hook was invisible to search_memories. build.py now writes DATA_DIR_NAME into each bundle's _harness_id.py and memory_core seeds its default from it, like the PLATFORM_* values. An end-to-end test launches the real session-start hook and MCP server for Claude Code, Cursor and Codex the way each host does, with the key in plugin settings and in the mem0 init config, and checks the search reaches the API with that key. Bumps the Python plugins to 0.3.4, OpenCode to 0.4.2, Pi and DeepSeek to 0.3.3, with changelog entries for each. Fixes #7346 --- .claude-plugin/marketplace.json | 2 +- .cursor-plugin/marketplace.json | 2 +- .kimi-plugin/marketplace.json | 2 +- docs/changelog/sdk.mdx | 77 +++++++++ integrations/agent-plugin-core/build/build.py | 1 + .../agent-plugin-core/python/memory_core.py | 25 +-- .../tests/test_mcp_server_auth.py | 150 ++++++++++++++++++ .../tests/test_uninitialised_identity.py | 20 +++ .../antigravity-plugin/core/_harness_id.py | 1 + .../antigravity-plugin/core/memory_core.py | 25 +-- .../antigravity-plugin/plugin-build.json | 2 +- .../.claude-plugin/plugin.json | 2 +- .../claude-code-plugin/core/_harness_id.py | 1 + .../claude-code-plugin/core/memory_core.py | 25 +-- .../claude-code-plugin/plugin-build.json | 2 +- .../codex-plugin/.codex-plugin/plugin.json | 2 +- integrations/codex-plugin/core/_harness_id.py | 1 + integrations/codex-plugin/core/memory_core.py | 25 +-- integrations/codex-plugin/plugin-build.json | 2 +- .../cursor-plugin/.cursor-plugin/plugin.json | 2 +- .../cursor-plugin/core/_harness_id.py | 1 + .../cursor-plugin/core/memory_core.py | 25 +-- integrations/cursor-plugin/plugin-build.json | 2 +- integrations/deepseek-plugin/package.json | 2 +- integrations/kimi-plugin/core/_harness_id.py | 1 + integrations/kimi-plugin/core/memory_core.py | 25 +-- integrations/kimi-plugin/kimi.plugin.json | 2 +- integrations/kimi-plugin/plugin-build.json | 2 +- .../mem0-agent-plugin/core/_harness_id.py | 1 + .../mem0-agent-plugin/core/memory_core.py | 25 +-- integrations/mem0-agent-plugin/plugin.json | 2 +- integrations/opencode-plugin/package.json | 2 +- integrations/pi-agent-plugin/package.json | 2 +- marketplace.json | 2 +- 34 files changed, 362 insertions(+), 101 deletions(-) create mode 100644 integrations/agent-plugin-core/tests/test_mcp_server_auth.py diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 752311cbc..8cf9ac4b9 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -12,7 +12,7 @@ "name": "mem0", "source": "./integrations/claude-code-plugin", "description": "Cross-session memory and token savings for coding agents.", - "version": "0.3.3" + "version": "0.3.4" } ] } diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 4d083a06b..e5af7ea61 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -12,7 +12,7 @@ "name": "mem0", "source": "./integrations/cursor-plugin", "description": "Cross-session memory and token savings for coding agents.", - "version": "0.3.3" + "version": "0.3.4" } ] } diff --git a/.kimi-plugin/marketplace.json b/.kimi-plugin/marketplace.json index 47c986c6c..2dbf5286d 100644 --- a/.kimi-plugin/marketplace.json +++ b/.kimi-plugin/marketplace.json @@ -5,7 +5,7 @@ { "id": "mem0", "displayName": "Mem0", - "version": "0.3.3", + "version": "0.3.4", "description": "Cross-session memory and token savings for coding agents.", "homepage": "https://mem0.ai", "keywords": ["memory", "personalization", "mcp", "semantic-search"], diff --git a/docs/changelog/sdk.mdx b/docs/changelog/sdk.mdx index 5598d0d96..7df38bfe8 100644 --- a/docs/changelog/sdk.mdx +++ b/docs/changelog/sdk.mdx @@ -2100,6 +2100,16 @@ A full-featured command-line interface for Mem0, available in both Python and No + + +**Fixes:** +- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when no key is set in the plugin settings or `MEM0_API_KEY`, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449)) +- **Auth:** A plugin setting that reaches the plugin as an unexpanded `${api_key}` or `${user_id}` placeholder is treated as unset. It is no longer sent as the API key, cached to disk, or used as the user ID ([#7449](https://github.com/mem0ai/mem0/pull/7449)) +- **State:** The `search_memories` MCP server now keeps its state in `~/.mem0/coding-agent-plugin`, the same directory the status, pause, resume and forget skills use, instead of `~/.mem0/mem0-plugin` ([#7449](https://github.com/mem0ai/mem0/pull/7449)) +- **Packaging:** `PLUGIN_VERSION` bumped to `0.3.4`, so the `mem0-plugin/` wire header and `plugin_version` telemetry field identify builds with this fix ([#7449](https://github.com/mem0ai/mem0/pull/7449)) + + + **Changed:** @@ -2411,6 +2421,15 @@ Initial release of the Mem0 plugin for Claude Code and Cursor, followed by Codex + + +**Fixes:** +- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when no key is set in the plugin settings or `MEM0_API_KEY`, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449)) +- **Auth:** A plugin setting that reaches the plugin as an unexpanded `${api_key}` or `${user_id}` placeholder is treated as unset. It is no longer sent as the API key, cached to disk, or used as the user ID ([#7449](https://github.com/mem0ai/mem0/pull/7449)) +- **Packaging:** `PLUGIN_VERSION` bumped to `0.3.4`, so the `mem0-plugin/` wire header and `plugin_version` telemetry field identify builds with this fix ([#7449](https://github.com/mem0ai/mem0/pull/7449)) + + + **Improvements:** @@ -2452,6 +2471,16 @@ Initial release of the Mem0 plugin for Claude Code and Cursor, followed by Codex + + +**Fixes:** +- **Auth:** An API key set in the Cursor plugin settings now reaches the `search_memories` MCP server. The server looked for the cached key in `~/.mem0/mem0-plugin` while the hooks cached it in `~/.mem0/cursor-plugin`, so search asked for auth whenever Cursor did not pass the setting to the MCP server directly ([#7449](https://github.com/mem0ai/mem0/pull/7449)) +- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when no key is set in the plugin settings or `MEM0_API_KEY`, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449)) +- **Auth:** A plugin setting that reaches the plugin as an unexpanded `${api_key}` or `${user_id}` placeholder is treated as unset. It is no longer sent as the API key, cached to disk, or used as the user ID ([#7449](https://github.com/mem0ai/mem0/pull/7449)) +- **Packaging:** `PLUGIN_VERSION` bumped to `0.3.4`, so the `mem0-plugin/` wire header and `plugin_version` telemetry field identify builds with this fix ([#7449](https://github.com/mem0ai/mem0/pull/7449)) + + + **Improvements:** @@ -2492,6 +2521,14 @@ Initial release of the Mem0 plugin for Claude Code and Cursor, followed by Codex + + +**Fixes:** +- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when `MEM0_API_KEY` is not set, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449)) +- **Packaging:** `PLUGIN_VERSION` bumped to `0.3.4`, so the `mem0-plugin/` wire header and `plugin_version` telemetry field identify builds with this fix ([#7449](https://github.com/mem0ai/mem0/pull/7449)) + + + **Improvements:** @@ -2531,6 +2568,13 @@ Initial release of the Mem0 plugin for Claude Code and Cursor, followed by Codex + + +**Fixes:** +- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when neither `MEM0_API_KEY` nor a shell profile sets one, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449)) + + + **Improvements:** @@ -2646,6 +2690,15 @@ Initial release of the Mem0 plugin for Claude Code and Cursor, followed by Codex + + +**Fixes:** +- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when `MEM0_API_KEY` is not set, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449)) +- **Data directory:** The `search_memories` MCP server uses `~/.mem0/antigravity-plugin`, the data directory the hooks use, instead of `~/.mem0/mem0-plugin` ([#7449](https://github.com/mem0ai/mem0/pull/7449)) +- **Packaging:** `PLUGIN_VERSION` bumped to `0.3.4`, so the `mem0-plugin/` wire header and `plugin_version` telemetry field identify builds with this fix ([#7449](https://github.com/mem0ai/mem0/pull/7449)) + + + **Improvements:** @@ -2759,6 +2812,15 @@ Existing memories written by the previous versions are not rewritten. If your me + + +**Fixes:** +- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when `MEM0_API_KEY` is not set, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449)) +- **Data directory:** The `search_memories` MCP server uses `~/.mem0/kimi-plugin`, the data directory the hooks use, instead of `~/.mem0/mem0-plugin` ([#7449](https://github.com/mem0ai/mem0/pull/7449)) +- **Packaging:** `PLUGIN_VERSION` bumped to `0.3.4`, so the `mem0-plugin/` wire header and `plugin_version` telemetry field identify builds with this fix ([#7449](https://github.com/mem0ai/mem0/pull/7449)) + + + **Improvements:** @@ -3117,6 +3179,13 @@ Existing memories written by the previous versions are not rewritten. If your me + + +**Fixes:** +- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when neither `MEM0_API_KEY` nor `~/.pi/agent/mem0-config.json` sets one, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449)) + + + **Improvements:** @@ -3246,6 +3315,14 @@ Existing memories written by the previous versions are not rewritten. If your me + + +**Fixes:** +- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when neither `config.apiKey` nor `MEM0_API_KEY` is set, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449)) +- **Config:** An empty `apiKey` now counts as unset and falls through to `MEM0_API_KEY` and the `mem0 init` key, instead of failing validation ([#7449](https://github.com/mem0ai/mem0/pull/7449)) + + + **Improvements:** diff --git a/integrations/agent-plugin-core/build/build.py b/integrations/agent-plugin-core/build/build.py index 3059b9b1f..8926a0edd 100644 --- a/integrations/agent-plugin-core/build/build.py +++ b/integrations/agent-plugin-core/build/build.py @@ -103,6 +103,7 @@ def _render_harness_id(host: str, *, portable: bool = False) -> str: "\n" f'HARNESS_ID = "{host}"\n' f'SOURCE_TAG = "{tag}"\n' + f'DATA_DIR_NAME = "{host}-plugin"\n' "\n" "# Platform-side vocabulary (mem0_event.source + X-Application). The whole\n" "# plugin family is one source; which editor it runs in is the application.\n" diff --git a/integrations/agent-plugin-core/python/memory_core.py b/integrations/agent-plugin-core/python/memory_core.py index 7695f0ac0..b045d3093 100644 --- a/integrations/agent-plugin-core/python/memory_core.py +++ b/integrations/agent-plugin-core/python/memory_core.py @@ -28,12 +28,23 @@ from typing import Any, Iterable import telemetry +# Read from the generated per-host module so a new entrypoint is correct without +# remembering to configure anything. +try: # pragma: no cover - absent only in the un-built shared source tree + from _harness_id import DATA_DIR_NAME as _DATA_DIR_NAME + from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION + from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE +except ImportError: + _DATA_DIR_NAME = "mem0-plugin" + _PLATFORM_SOURCE = "MEM0_PLUGIN" + _PLATFORM_APPLICATION = "" + DEFAULT_API_URL = "https://api.mem0.ai" -PLUGIN_VERSION = "0.3.3" +PLUGIN_VERSION = "0.3.4" _harness_name: str = "generic" _harness_env_prefix: str = "MEM0_PLUGIN" -_harness_data_dir_name: str = "mem0-plugin" +_harness_data_dir_name: str = _DATA_DIR_NAME _harness_source_tag: str = "mem0_plugin" @@ -1802,16 +1813,6 @@ def extraction_message_batches( return batches -# Platform surface attribution. Read from the generated per-host module so a new -# entrypoint is correct without remembering to configure anything. -try: # pragma: no cover - absent only in the un-built shared source tree - from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION - from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE -except ImportError: - _PLATFORM_SOURCE = "MEM0_PLUGIN" - _PLATFORM_APPLICATION = "" - - def platform_headers(key: str) -> dict[str, str]: """Auth plus the three surface-identity headers. diff --git a/integrations/agent-plugin-core/tests/test_mcp_server_auth.py b/integrations/agent-plugin-core/tests/test_mcp_server_auth.py new file mode 100644 index 000000000..9e00b67d8 --- /dev/null +++ b/integrations/agent-plugin-core/tests/test_mcp_server_auth.py @@ -0,0 +1,150 @@ +"""Launch each host's real hooks and MCP server the way the host does, and check the search is authenticated.""" + +from __future__ import annotations + +import json +import os +import subprocess +import sys +import threading +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from string import Template + +import pytest + +INTEGRATIONS = Path(__file__).resolve().parents[2] +KEY = "m0-plugin-setting-key" +CODEX_INHERITED_ENV = ("HOME", "PATH", "LANG", "TMPDIR") + + +class _Mem0Api(BaseHTTPRequestHandler): + authorizations: list[str] + + def do_POST(self) -> None: + self.rfile.read(int(self.headers.get("Content-Length") or 0)) + self.authorizations.append(self.headers.get("Authorization", "")) + body = b'{"results": []}' + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + def log_message(self, *args: object) -> None: + pass + + +@pytest.fixture +def mem0_api(): + handler = type("Handler", (_Mem0Api,), {"authorizations": []}) + server = ThreadingHTTPServer(("127.0.0.1", 0), handler) + threading.Thread(target=server.serve_forever, daemon=True).start() + yield f"http://127.0.0.1:{server.server_port}", handler.authorizations + server.shutdown() + + +def _json(path: Path) -> dict: + return json.loads(path.read_text(encoding="utf-8")) + + +def _expand(value: str, variables: dict[str, str]) -> str: + return Template(value).safe_substitute(variables) + + +def _run_hook(command: str, cwd: Path, env: dict[str, str], payload: dict) -> None: + result = subprocess.run( + ["sh", "-c", command], cwd=cwd, env=env, input=json.dumps(payload), text=True, capture_output=True + ) + assert result.returncode == 0, result.stderr + + +def _search(argv: list[str], cwd: Path, env: dict[str, str]) -> dict: + requests = [ + {"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2024-11-05"}}, + { + "jsonrpc": "2.0", + "id": 2, + "method": "tools/call", + "params": {"name": "search_memories", "arguments": {"query": "earlier fixes"}}, + }, + ] + result = subprocess.run( + argv, + cwd=cwd, + env=env, + input="\n".join(json.dumps(request) for request in requests) + "\n", + text=True, + capture_output=True, + timeout=30, + ) + assert result.returncode == 0, result.stderr + return json.loads(result.stdout.splitlines()[-1]) + + +def _claude_code(host_env: dict[str, str], tmp_path: Path, repo: Path, setting: bool) -> tuple: + root = INTEGRATIONS / "claude-code-plugin" + plugin_env = {"CLAUDE_PLUGIN_ROOT": str(root), "CLAUDE_PLUGIN_DATA": str(tmp_path / "claude-data")} + hook_env = {**host_env, **plugin_env, **({"CLAUDE_PLUGIN_OPTION_API_KEY": KEY} if setting else {})} + command = _json(root / "hooks" / "hooks.json")["hooks"]["SessionStart"][0]["hooks"][0]["command"] + _run_hook(command, repo, hook_env, {"session_id": "s1", "cwd": str(repo)}) + + server = _json(root / ".mcp.json")["mcpServers"]["mem0"] + env = {**host_env, **{name: _expand(value, plugin_env) for name, value in server["env"].items()}} + return [sys.executable, *(_expand(argument, plugin_env) for argument in server["args"])], repo, env + + +def _cursor(host_env: dict[str, str], tmp_path: Path, repo: Path, setting: bool) -> tuple: + root = INTEGRATIONS / "cursor-plugin" + command = _json(root / "hooks" / "hooks.json")["hooks"]["sessionStart"][0]["command"] + if setting: + command = command.replace("${api_key}", KEY) + hook_env = {**host_env, "CURSOR_PLUGIN_ROOT": str(root)} + _run_hook(command, repo, hook_env, {"conversation_id": "c1", "workspace_roots": [str(repo)]}) + + server = _json(root / "mcp.json")["mcpServers"]["mem0"] + args = [_expand(argument, {"CURSOR_PLUGIN_ROOT": str(root)}) for argument in server["args"]] + return [sys.executable, *args], repo, {**host_env, **server["env"]} + + +def _codex(host_env: dict[str, str], tmp_path: Path, repo: Path, setting: bool) -> tuple: + root = INTEGRATIONS / "codex-plugin" + codex_env = {**host_env, **({"MEM0_API_KEY": KEY} if setting else {})} + data = str(tmp_path / "codex-data") + hook_env = {**codex_env, "PLUGIN_ROOT": str(root), "PLUGIN_DATA": data, "CLAUDE_PLUGIN_DATA": data} + command = _json(root / "hooks" / "hooks.json")["hooks"]["SessionStart"][0]["hooks"][0]["command"] + _run_hook(command, repo, hook_env, {"session_id": "s1", "cwd": str(repo)}) + + server = _json(root / ".mcp.json")["mcpServers"]["mem0"] + forwarded = [*CODEX_INHERITED_ENV, *server["env_vars"]] + env = {name: codex_env[name] for name in forwarded if name in codex_env} + return [sys.executable, *server["args"]], root / server["cwd"], env + + +HOSTS = {"claude-code": _claude_code, "cursor": _cursor, "codex": _codex} + + +@pytest.mark.parametrize("key_source", ["plugin setting", "mem0 init"]) +@pytest.mark.parametrize("host", sorted(HOSTS)) +def test_mcp_server_searches_with_the_key_the_user_configured(host, key_source, tmp_path, mem0_api): + """The key reaches the MCP server however the host delivers it: plugin setting, env, or `mem0 init`.""" + api_url, authorizations = mem0_api + home = tmp_path / "home" + repo = tmp_path / "repo" + repo.mkdir() + if key_source == "mem0 init": + (home / ".mem0").mkdir(parents=True) + (home / ".mem0" / "config.json").write_text(json.dumps({"platform": {"api_key": KEY}}), encoding="utf-8") + host_env = { + "HOME": str(home), + "PATH": os.environ["PATH"], + "MEM0_API_URL": api_url, + "MEM0_TELEMETRY": "false", + "MEM0_CODE_USER_ID": "test-user", + } + + argv, cwd, env = HOSTS[host](host_env, tmp_path, repo, key_source == "plugin setting") + response = _search(argv, cwd, env) + + assert not response["result"].get("isError"), response + assert authorizations == [f"Token {KEY}"] diff --git a/integrations/agent-plugin-core/tests/test_uninitialised_identity.py b/integrations/agent-plugin-core/tests/test_uninitialised_identity.py index 74a31fa5c..f7be32132 100644 --- a/integrations/agent-plugin-core/tests/test_uninitialised_identity.py +++ b/integrations/agent-plugin-core/tests/test_uninitialised_identity.py @@ -83,6 +83,26 @@ def test_identity_resolves_without_init(harness, spec): assert out == f"{harness} {source_tag}" +@pytest.mark.parametrize("harness,spec", sorted(HOSTS.items())) +def test_data_dir_resolves_without_init(harness, spec): + """mcp_server never configures the harness, so its default must match configure_harness(harness) in hooks and skills.""" + directory, _ = spec + core = _core_dir(directory) + if not core.exists(): + pytest.skip(f"{directory} is not built in this tree") + + with tempfile.TemporaryDirectory() as tmp: + out = _run( + core, + Path(tmp), + "import os; os.environ.pop('MEM0_CODE_DATA_DIR')\n" + "import mcp_server, memory_core; print(memory_core.data_dir())\n" + f"memory_core.configure_harness({harness!r}); print(memory_core.data_dir())", + ) + expected = str(Path(tmp) / ".mem0" / f"{harness}-plugin") + assert out.splitlines() == [expected, expected] + + def test_mcp_server_records_the_real_harness(): """mcp_server imports telemetry and never initialises it (server.py has no init). diff --git a/integrations/antigravity-plugin/core/_harness_id.py b/integrations/antigravity-plugin/core/_harness_id.py index 18b7f68ec..80fef7571 100644 --- a/integrations/antigravity-plugin/core/_harness_id.py +++ b/integrations/antigravity-plugin/core/_harness_id.py @@ -2,6 +2,7 @@ HARNESS_ID = "antigravity" SOURCE_TAG = "ANTIGRAVITY_PLUGIN" +DATA_DIR_NAME = "antigravity-plugin" # Platform-side vocabulary (mem0_event.source + X-Application). The whole # plugin family is one source; which editor it runs in is the application. diff --git a/integrations/antigravity-plugin/core/memory_core.py b/integrations/antigravity-plugin/core/memory_core.py index 7695f0ac0..b045d3093 100644 --- a/integrations/antigravity-plugin/core/memory_core.py +++ b/integrations/antigravity-plugin/core/memory_core.py @@ -28,12 +28,23 @@ from typing import Any, Iterable import telemetry +# Read from the generated per-host module so a new entrypoint is correct without +# remembering to configure anything. +try: # pragma: no cover - absent only in the un-built shared source tree + from _harness_id import DATA_DIR_NAME as _DATA_DIR_NAME + from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION + from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE +except ImportError: + _DATA_DIR_NAME = "mem0-plugin" + _PLATFORM_SOURCE = "MEM0_PLUGIN" + _PLATFORM_APPLICATION = "" + DEFAULT_API_URL = "https://api.mem0.ai" -PLUGIN_VERSION = "0.3.3" +PLUGIN_VERSION = "0.3.4" _harness_name: str = "generic" _harness_env_prefix: str = "MEM0_PLUGIN" -_harness_data_dir_name: str = "mem0-plugin" +_harness_data_dir_name: str = _DATA_DIR_NAME _harness_source_tag: str = "mem0_plugin" @@ -1802,16 +1813,6 @@ def extraction_message_batches( return batches -# Platform surface attribution. Read from the generated per-host module so a new -# entrypoint is correct without remembering to configure anything. -try: # pragma: no cover - absent only in the un-built shared source tree - from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION - from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE -except ImportError: - _PLATFORM_SOURCE = "MEM0_PLUGIN" - _PLATFORM_APPLICATION = "" - - def platform_headers(key: str) -> dict[str, str]: """Auth plus the three surface-identity headers. diff --git a/integrations/antigravity-plugin/plugin-build.json b/integrations/antigravity-plugin/plugin-build.json index 5a9112025..7fdbbd744 100644 --- a/integrations/antigravity-plugin/plugin-build.json +++ b/integrations/antigravity-plugin/plugin-build.json @@ -1,6 +1,6 @@ { "id": "mem0", - "version": "0.3.3", + "version": "0.3.4", "homepage": "https://docs.mem0.ai/integrations/antigravity", "native": { "pluginRoot": "${ANTIGRAVITY_PLUGIN_ROOT}", diff --git a/integrations/claude-code-plugin/.claude-plugin/plugin.json b/integrations/claude-code-plugin/.claude-plugin/plugin.json index 8182b49dc..25f4f8bd1 100644 --- a/integrations/claude-code-plugin/.claude-plugin/plugin.json +++ b/integrations/claude-code-plugin/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "mem0", - "version": "0.3.3", + "version": "0.3.4", "description": "Cross-session memory and token savings for coding agents.", "author": { "name": "Mem0" diff --git a/integrations/claude-code-plugin/core/_harness_id.py b/integrations/claude-code-plugin/core/_harness_id.py index 9c4949408..ea64fb625 100644 --- a/integrations/claude-code-plugin/core/_harness_id.py +++ b/integrations/claude-code-plugin/core/_harness_id.py @@ -2,6 +2,7 @@ HARNESS_ID = "claude-code" SOURCE_TAG = "CLAUDE_CODE_PLUGIN" +DATA_DIR_NAME = "claude-code-plugin" # Platform-side vocabulary (mem0_event.source + X-Application). The whole # plugin family is one source; which editor it runs in is the application. diff --git a/integrations/claude-code-plugin/core/memory_core.py b/integrations/claude-code-plugin/core/memory_core.py index 7695f0ac0..b045d3093 100644 --- a/integrations/claude-code-plugin/core/memory_core.py +++ b/integrations/claude-code-plugin/core/memory_core.py @@ -28,12 +28,23 @@ from typing import Any, Iterable import telemetry +# Read from the generated per-host module so a new entrypoint is correct without +# remembering to configure anything. +try: # pragma: no cover - absent only in the un-built shared source tree + from _harness_id import DATA_DIR_NAME as _DATA_DIR_NAME + from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION + from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE +except ImportError: + _DATA_DIR_NAME = "mem0-plugin" + _PLATFORM_SOURCE = "MEM0_PLUGIN" + _PLATFORM_APPLICATION = "" + DEFAULT_API_URL = "https://api.mem0.ai" -PLUGIN_VERSION = "0.3.3" +PLUGIN_VERSION = "0.3.4" _harness_name: str = "generic" _harness_env_prefix: str = "MEM0_PLUGIN" -_harness_data_dir_name: str = "mem0-plugin" +_harness_data_dir_name: str = _DATA_DIR_NAME _harness_source_tag: str = "mem0_plugin" @@ -1802,16 +1813,6 @@ def extraction_message_batches( return batches -# Platform surface attribution. Read from the generated per-host module so a new -# entrypoint is correct without remembering to configure anything. -try: # pragma: no cover - absent only in the un-built shared source tree - from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION - from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE -except ImportError: - _PLATFORM_SOURCE = "MEM0_PLUGIN" - _PLATFORM_APPLICATION = "" - - def platform_headers(key: str) -> dict[str, str]: """Auth plus the three surface-identity headers. diff --git a/integrations/claude-code-plugin/plugin-build.json b/integrations/claude-code-plugin/plugin-build.json index d62d653c3..573e9ad20 100644 --- a/integrations/claude-code-plugin/plugin-build.json +++ b/integrations/claude-code-plugin/plugin-build.json @@ -1,6 +1,6 @@ { "id": "mem0", - "version": "0.3.3", + "version": "0.3.4", "homepage": "https://docs.mem0.ai/integrations/claude-code", "native": { "pluginRoot": "${CLAUDE_PLUGIN_ROOT}", diff --git a/integrations/codex-plugin/.codex-plugin/plugin.json b/integrations/codex-plugin/.codex-plugin/plugin.json index e91eca25a..044a1940c 100644 --- a/integrations/codex-plugin/.codex-plugin/plugin.json +++ b/integrations/codex-plugin/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "mem0", - "version": "0.3.3", + "version": "0.3.4", "description": "Cross-session memory and token savings for coding agents.", "author": { "name": "Mem0", "email": "support@mem0.ai" }, "homepage": "https://docs.mem0.ai/integrations/codex", diff --git a/integrations/codex-plugin/core/_harness_id.py b/integrations/codex-plugin/core/_harness_id.py index 47938ee08..96611e3a1 100644 --- a/integrations/codex-plugin/core/_harness_id.py +++ b/integrations/codex-plugin/core/_harness_id.py @@ -2,6 +2,7 @@ HARNESS_ID = "codex" SOURCE_TAG = "CODEX_PLUGIN" +DATA_DIR_NAME = "codex-plugin" # Platform-side vocabulary (mem0_event.source + X-Application). The whole # plugin family is one source; which editor it runs in is the application. diff --git a/integrations/codex-plugin/core/memory_core.py b/integrations/codex-plugin/core/memory_core.py index 7695f0ac0..b045d3093 100644 --- a/integrations/codex-plugin/core/memory_core.py +++ b/integrations/codex-plugin/core/memory_core.py @@ -28,12 +28,23 @@ from typing import Any, Iterable import telemetry +# Read from the generated per-host module so a new entrypoint is correct without +# remembering to configure anything. +try: # pragma: no cover - absent only in the un-built shared source tree + from _harness_id import DATA_DIR_NAME as _DATA_DIR_NAME + from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION + from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE +except ImportError: + _DATA_DIR_NAME = "mem0-plugin" + _PLATFORM_SOURCE = "MEM0_PLUGIN" + _PLATFORM_APPLICATION = "" + DEFAULT_API_URL = "https://api.mem0.ai" -PLUGIN_VERSION = "0.3.3" +PLUGIN_VERSION = "0.3.4" _harness_name: str = "generic" _harness_env_prefix: str = "MEM0_PLUGIN" -_harness_data_dir_name: str = "mem0-plugin" +_harness_data_dir_name: str = _DATA_DIR_NAME _harness_source_tag: str = "mem0_plugin" @@ -1802,16 +1813,6 @@ def extraction_message_batches( return batches -# Platform surface attribution. Read from the generated per-host module so a new -# entrypoint is correct without remembering to configure anything. -try: # pragma: no cover - absent only in the un-built shared source tree - from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION - from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE -except ImportError: - _PLATFORM_SOURCE = "MEM0_PLUGIN" - _PLATFORM_APPLICATION = "" - - def platform_headers(key: str) -> dict[str, str]: """Auth plus the three surface-identity headers. diff --git a/integrations/codex-plugin/plugin-build.json b/integrations/codex-plugin/plugin-build.json index a354c31f5..56ef202b0 100644 --- a/integrations/codex-plugin/plugin-build.json +++ b/integrations/codex-plugin/plugin-build.json @@ -1,6 +1,6 @@ { "id": "mem0", - "version": "0.3.3", + "version": "0.3.4", "homepage": "https://docs.mem0.ai/integrations/codex", "native": { "pluginRoot": "${PLUGIN_ROOT}", diff --git a/integrations/cursor-plugin/.cursor-plugin/plugin.json b/integrations/cursor-plugin/.cursor-plugin/plugin.json index 7c2304f29..2a3d6eeaf 100644 --- a/integrations/cursor-plugin/.cursor-plugin/plugin.json +++ b/integrations/cursor-plugin/.cursor-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "mem0", - "version": "0.3.3", + "version": "0.3.4", "description": "Cross-session memory and token savings for coding agents.", "author": { "name": "Mem0", "email": "support@mem0.ai" }, "homepage": "https://docs.mem0.ai/integrations/cursor", diff --git a/integrations/cursor-plugin/core/_harness_id.py b/integrations/cursor-plugin/core/_harness_id.py index 0e20bf1ff..c20d02785 100644 --- a/integrations/cursor-plugin/core/_harness_id.py +++ b/integrations/cursor-plugin/core/_harness_id.py @@ -2,6 +2,7 @@ HARNESS_ID = "cursor" SOURCE_TAG = "CURSOR_PLUGIN" +DATA_DIR_NAME = "cursor-plugin" # Platform-side vocabulary (mem0_event.source + X-Application). The whole # plugin family is one source; which editor it runs in is the application. diff --git a/integrations/cursor-plugin/core/memory_core.py b/integrations/cursor-plugin/core/memory_core.py index 7695f0ac0..b045d3093 100644 --- a/integrations/cursor-plugin/core/memory_core.py +++ b/integrations/cursor-plugin/core/memory_core.py @@ -28,12 +28,23 @@ from typing import Any, Iterable import telemetry +# Read from the generated per-host module so a new entrypoint is correct without +# remembering to configure anything. +try: # pragma: no cover - absent only in the un-built shared source tree + from _harness_id import DATA_DIR_NAME as _DATA_DIR_NAME + from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION + from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE +except ImportError: + _DATA_DIR_NAME = "mem0-plugin" + _PLATFORM_SOURCE = "MEM0_PLUGIN" + _PLATFORM_APPLICATION = "" + DEFAULT_API_URL = "https://api.mem0.ai" -PLUGIN_VERSION = "0.3.3" +PLUGIN_VERSION = "0.3.4" _harness_name: str = "generic" _harness_env_prefix: str = "MEM0_PLUGIN" -_harness_data_dir_name: str = "mem0-plugin" +_harness_data_dir_name: str = _DATA_DIR_NAME _harness_source_tag: str = "mem0_plugin" @@ -1802,16 +1813,6 @@ def extraction_message_batches( return batches -# Platform surface attribution. Read from the generated per-host module so a new -# entrypoint is correct without remembering to configure anything. -try: # pragma: no cover - absent only in the un-built shared source tree - from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION - from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE -except ImportError: - _PLATFORM_SOURCE = "MEM0_PLUGIN" - _PLATFORM_APPLICATION = "" - - def platform_headers(key: str) -> dict[str, str]: """Auth plus the three surface-identity headers. diff --git a/integrations/cursor-plugin/plugin-build.json b/integrations/cursor-plugin/plugin-build.json index 5320fb7f9..53890341a 100644 --- a/integrations/cursor-plugin/plugin-build.json +++ b/integrations/cursor-plugin/plugin-build.json @@ -1,6 +1,6 @@ { "id": "mem0", - "version": "0.3.3", + "version": "0.3.4", "homepage": "https://docs.mem0.ai/integrations/cursor", "native": { "pluginRoot": "${CURSOR_PLUGIN_ROOT}", diff --git a/integrations/deepseek-plugin/package.json b/integrations/deepseek-plugin/package.json index 41a8fe7a5..fbea2acf7 100644 --- a/integrations/deepseek-plugin/package.json +++ b/integrations/deepseek-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@mem0/deepseek-plugin", - "version": "0.3.2", + "version": "0.3.3", "description": "Mem0 long-term memory as a native DeepSeek Harness (Cordis) plugin.", "type": "module", "license": "Apache-2.0", diff --git a/integrations/kimi-plugin/core/_harness_id.py b/integrations/kimi-plugin/core/_harness_id.py index 83ab061ef..dc65e8288 100644 --- a/integrations/kimi-plugin/core/_harness_id.py +++ b/integrations/kimi-plugin/core/_harness_id.py @@ -2,6 +2,7 @@ HARNESS_ID = "kimi" SOURCE_TAG = "KIMI_PLUGIN" +DATA_DIR_NAME = "kimi-plugin" # Platform-side vocabulary (mem0_event.source + X-Application). The whole # plugin family is one source; which editor it runs in is the application. diff --git a/integrations/kimi-plugin/core/memory_core.py b/integrations/kimi-plugin/core/memory_core.py index 7695f0ac0..b045d3093 100644 --- a/integrations/kimi-plugin/core/memory_core.py +++ b/integrations/kimi-plugin/core/memory_core.py @@ -28,12 +28,23 @@ from typing import Any, Iterable import telemetry +# Read from the generated per-host module so a new entrypoint is correct without +# remembering to configure anything. +try: # pragma: no cover - absent only in the un-built shared source tree + from _harness_id import DATA_DIR_NAME as _DATA_DIR_NAME + from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION + from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE +except ImportError: + _DATA_DIR_NAME = "mem0-plugin" + _PLATFORM_SOURCE = "MEM0_PLUGIN" + _PLATFORM_APPLICATION = "" + DEFAULT_API_URL = "https://api.mem0.ai" -PLUGIN_VERSION = "0.3.3" +PLUGIN_VERSION = "0.3.4" _harness_name: str = "generic" _harness_env_prefix: str = "MEM0_PLUGIN" -_harness_data_dir_name: str = "mem0-plugin" +_harness_data_dir_name: str = _DATA_DIR_NAME _harness_source_tag: str = "mem0_plugin" @@ -1802,16 +1813,6 @@ def extraction_message_batches( return batches -# Platform surface attribution. Read from the generated per-host module so a new -# entrypoint is correct without remembering to configure anything. -try: # pragma: no cover - absent only in the un-built shared source tree - from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION - from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE -except ImportError: - _PLATFORM_SOURCE = "MEM0_PLUGIN" - _PLATFORM_APPLICATION = "" - - def platform_headers(key: str) -> dict[str, str]: """Auth plus the three surface-identity headers. diff --git a/integrations/kimi-plugin/kimi.plugin.json b/integrations/kimi-plugin/kimi.plugin.json index 6b28575d3..31de0ba13 100644 --- a/integrations/kimi-plugin/kimi.plugin.json +++ b/integrations/kimi-plugin/kimi.plugin.json @@ -1,6 +1,6 @@ { "name": "mem0", - "version": "0.3.3", + "version": "0.3.4", "description": "Cross-session memory and token savings for coding agents.", "keywords": ["memory", "coding-agents", "continual-learning", "token-efficiency"], "author": { "name": "Mem0", "email": "support@mem0.ai" }, diff --git a/integrations/kimi-plugin/plugin-build.json b/integrations/kimi-plugin/plugin-build.json index 35e38ecf5..904c20ad2 100644 --- a/integrations/kimi-plugin/plugin-build.json +++ b/integrations/kimi-plugin/plugin-build.json @@ -1,6 +1,6 @@ { "id": "mem0", - "version": "0.3.3", + "version": "0.3.4", "homepage": "https://docs.mem0.ai/integrations/kimi", "native": { "pluginRoot": "${KIMI_PLUGIN_ROOT}", diff --git a/integrations/mem0-agent-plugin/core/_harness_id.py b/integrations/mem0-agent-plugin/core/_harness_id.py index 3efd000f8..6b951e1f3 100644 --- a/integrations/mem0-agent-plugin/core/_harness_id.py +++ b/integrations/mem0-agent-plugin/core/_harness_id.py @@ -2,6 +2,7 @@ HARNESS_ID = "coding-agent" SOURCE_TAG = "CODING_AGENT_PLUGIN" +DATA_DIR_NAME = "coding-agent-plugin" # Platform-side vocabulary (mem0_event.source + X-Application). The whole # plugin family is one source; which editor it runs in is the application. diff --git a/integrations/mem0-agent-plugin/core/memory_core.py b/integrations/mem0-agent-plugin/core/memory_core.py index 7695f0ac0..b045d3093 100644 --- a/integrations/mem0-agent-plugin/core/memory_core.py +++ b/integrations/mem0-agent-plugin/core/memory_core.py @@ -28,12 +28,23 @@ from typing import Any, Iterable import telemetry +# Read from the generated per-host module so a new entrypoint is correct without +# remembering to configure anything. +try: # pragma: no cover - absent only in the un-built shared source tree + from _harness_id import DATA_DIR_NAME as _DATA_DIR_NAME + from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION + from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE +except ImportError: + _DATA_DIR_NAME = "mem0-plugin" + _PLATFORM_SOURCE = "MEM0_PLUGIN" + _PLATFORM_APPLICATION = "" + DEFAULT_API_URL = "https://api.mem0.ai" -PLUGIN_VERSION = "0.3.3" +PLUGIN_VERSION = "0.3.4" _harness_name: str = "generic" _harness_env_prefix: str = "MEM0_PLUGIN" -_harness_data_dir_name: str = "mem0-plugin" +_harness_data_dir_name: str = _DATA_DIR_NAME _harness_source_tag: str = "mem0_plugin" @@ -1802,16 +1813,6 @@ def extraction_message_batches( return batches -# Platform surface attribution. Read from the generated per-host module so a new -# entrypoint is correct without remembering to configure anything. -try: # pragma: no cover - absent only in the un-built shared source tree - from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION - from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE -except ImportError: - _PLATFORM_SOURCE = "MEM0_PLUGIN" - _PLATFORM_APPLICATION = "" - - def platform_headers(key: str) -> dict[str, str]: """Auth plus the three surface-identity headers. diff --git a/integrations/mem0-agent-plugin/plugin.json b/integrations/mem0-agent-plugin/plugin.json index 95ff54651..b3f8fe384 100644 --- a/integrations/mem0-agent-plugin/plugin.json +++ b/integrations/mem0-agent-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", "name": "mem0", - "version": "0.3.3", + "version": "0.3.4", "description": "Cross-session memory and token savings for coding agents.", "author": { "name": "Mem0", diff --git a/integrations/opencode-plugin/package.json b/integrations/opencode-plugin/package.json index 36c3dce6b..497b8d0c0 100644 --- a/integrations/opencode-plugin/package.json +++ b/integrations/opencode-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@mem0/opencode-plugin", - "version": "0.4.1", + "version": "0.4.2", "type": "module", "description": "Mem0 persistent memory plugin for OpenCode — add, search, and manage memories across sessions", "main": "dist/index.js", diff --git a/integrations/pi-agent-plugin/package.json b/integrations/pi-agent-plugin/package.json index fbec4db1b..026d2d6b9 100644 --- a/integrations/pi-agent-plugin/package.json +++ b/integrations/pi-agent-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@mem0/pi-agent-plugin", - "version": "0.3.2", + "version": "0.3.3", "type": "module", "description": "Mem0 memory extension for Pi Agent persistent, scoped, semantic memory across sessions and projects", "license": "Apache-2.0", diff --git a/marketplace.json b/marketplace.json index ded2d2ece..9aab653bb 100644 --- a/marketplace.json +++ b/marketplace.json @@ -13,7 +13,7 @@ }, "category": "Productivity", "description": "Cross-session memory and token savings for coding agents.", - "version": "0.3.3" + "version": "0.3.4" } ] }