feat(integrations): add Hermes provider with shared memory utilities

This commit is contained in:
kartik-mem0
2026-09-18 13:21:43 +05:30
parent 84bf468176
commit 4284ef0d2a
40 changed files with 4168 additions and 954 deletions
@@ -15,6 +15,7 @@ from build.build import ( # noqa: E402
bundle_drift,
render_template,
replace_output,
sync_generated,
)
from build.validate import validate_bundle # noqa: E402
@@ -113,6 +114,7 @@ def test_native_control_skills_select_the_host_store(host: str, tmp_path: Path)
("codex", "native"),
("kimi", "native"),
("antigravity", "native"),
("hermes", "native"),
],
)
def test_installable_plugin_directories_are_current(host: str, kind: str) -> None:
@@ -133,3 +135,91 @@ def test_marketplaces_keep_public_names_and_reference_real_plugins() -> None:
assert [plugin["name"] for plugin in codex_marketplace["plugins"]] == ["mem0"]
codex = codex_marketplace["plugins"][0]
assert codex["source"]["path"] == "./integrations/codex-plugin"
def test_native_bundle_can_select_runtime_without_skills(tmp_path: Path, monkeypatch) -> None:
from build import build as builder
source = tmp_path / "plugin"
source.mkdir()
(source / "__init__.py").write_text("# Native plugin adapter\n", encoding="utf-8")
(source / "plugin-build.json").write_text(
json.dumps(
{
"native": {
"pluginRoot": "${PLUGIN_ROOT}",
"pythonFiles": ["message_utils.py"],
"skills": False,
"files": {"__init__.py": "__init__.py"},
}
}
),
encoding="utf-8",
)
monkeypatch.setitem(builder.NATIVE_PLUGINS, "test-host", source)
stale_skill = source / "skills" / "remember" / "SKILL.md"
stale_skill.parent.mkdir(parents=True)
stale_skill.write_text("stale generated skill", encoding="utf-8")
root = build("test-host", "native", tmp_path / "output")
assert {path.name for path in (root / "core").iterdir()} == {"message_utils.py"}
assert not (root / "skills").exists()
assert (root / "__init__.py").read_text(encoding="utf-8") == "# Native plugin adapter\n"
sync_generated("test-host", "native")
assert bundle_drift("test-host", "native") == []
assert not (source / "skills").exists()
@pytest.mark.parametrize("files", ["message_utils.py", ["../README.md"], ["/tmp/source.py"], [7], ["missing.py"]])
def test_native_runtime_selection_rejects_invalid_sources(files, tmp_path: Path) -> None:
from build.build import _build_native
with pytest.raises(ValueError, match="pythonFiles|native source file"):
_build_native(
"test",
tmp_path,
tmp_path,
{
"native": {
"pluginRoot": "${PLUGIN_ROOT}",
"pythonFiles": files,
}
},
)
def test_native_runtime_selection_rejects_escaping_symlink(tmp_path: Path, monkeypatch) -> None:
from build import build as builder
source = tmp_path / "shared" / "python"
source.mkdir(parents=True)
secret = tmp_path / "outside.py"
secret.write_text("secret", encoding="utf-8")
(source / "message_utils.py").symlink_to(secret)
monkeypatch.setattr(builder, "CORE_ROOT", source.parent)
staged = tmp_path / "bundle"
staged.mkdir()
with pytest.raises(ValueError, match="inside their roots"):
builder._build_native(
"test",
tmp_path,
staged,
{
"native": {
"pluginRoot": "${PLUGIN_ROOT}",
"pythonFiles": ["message_utils.py"],
}
},
)
def test_hermes_bundle_uses_only_host_independent_runtime(tmp_path: Path) -> None:
root = build("hermes", "native", tmp_path / "hermes")
assert {path.name for path in (root / "core").iterdir()} == {"message_utils.py"}
assert (root / "__init__.py").is_file()
assert (root / "plugin.yaml").is_file()
assert not (root / "skills").exists()
assert not (root / "agents").exists()
@@ -10,10 +10,9 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from conformance import run as conformance_run # noqa: E402
from conformance.run import _command_check # noqa: E402
PLUGIN_ROOT = Path(__file__).resolve().parents[1]
RUNNER = PLUGIN_ROOT / "conformance" / "run.py"
PYTHON_HOSTS = {"claude-code", "cursor", "codex", "kimi", "antigravity"}
PYTHON_HOSTS = {"claude-code", "cursor", "codex", "kimi", "antigravity", "hermes"}
def test_python_bundle_conformance_builds_every_host(tmp_path: Path) -> None:
@@ -78,6 +77,12 @@ def test_conformance_plan_covers_every_runtime(tmp_path: Path) -> None:
"deepseek",
}
assert all(entry["status"] == "planned" for entry in payload["checks"])
python_commands = [entry["command"] for entry in payload["checks"] if entry["group"] == "python-tests"]
assert any(
"integrations/hermes-plugin/tests" in command
and "--confcutdir=integrations/hermes-plugin/tests" in command
for command in python_commands
)
assert {
entry["group"]
for entry in payload["checks"]
@@ -0,0 +1,39 @@
from __future__ import annotations
import subprocess
import sys
from pathlib import Path
CORE = Path(__file__).resolve().parents[1] / "python"
def test_message_helpers_are_standalone_and_keep_legacy_exports() -> None:
result = subprocess.run(
[
sys.executable,
"-c",
"""
import json
import sys
import message_utils
assert "memory_core" not in sys.modules
assert "telemetry" not in sys.modules
text = "prefix " + "日本語🙂" * 250 + ' {"password": "secret value"}'
redacted = message_utils.redact(text)
assert "secret value" not in redacted
messages = [{"role": "user", "content": redacted}]
batches = message_utils.extraction_message_batches(messages, max_tokens=100)
assert len(batches) > 1
assert all(message_utils._message_tokens(batch) <= 100 for batch in batches)
assert "".join(m["content"] for batch in batches for m in batch) == redacted
import memory_core
for name in ("redact", "SECRET_PATTERNS", "extraction_message_batches", "_message_tokens"):
assert getattr(memory_core, name) is getattr(message_utils, name)
""",
],
cwd=CORE,
text=True,
capture_output=True,
check=False,
)
assert result.returncode == 0, result.stdout + result.stderr