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" } ] }