diff --git a/hermes_cli/config.py b/hermes_cli/config.py index d0ef0c3a205..71d23a2c138 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -3649,6 +3649,83 @@ OPTIONAL_ENV_VARS = { "category": "tool", }, + # ── Hindsight ── + "HINDSIGHT_API_KEY": { + "description": "Hindsight API key for graph-aware persistent memory", + "prompt": "Hindsight API key", + "url": "https://hindsight.vectorize.io", + "tools": ["hindsight_recall"], + "password": True, + "category": "tool", + }, + "HINDSIGHT_API_URL": { + "description": "Base URL for the Hindsight API (default: https://api.hindsight.vectorize.io)", + "prompt": "Hindsight API URL", + "category": "tool", + "advanced": True, + }, + + # ── Supermemory ── + "SUPERMEMORY_API_KEY": { + "description": "Supermemory API key for conversation-scoped persistent memory", + "prompt": "Supermemory API key", + "url": "https://supermemory.ai", + "tools": ["supermemory_search"], + "password": True, + "category": "tool", + }, + + # ── Mem0 ── + "MEM0_API_KEY": { + "description": "Mem0 Platform API key for semantic persistent memory", + "prompt": "Mem0 API key", + "url": "https://app.mem0.ai", + "tools": ["mem0_search"], + "password": True, + "category": "tool", + }, + + # ── RetainDB ── + "RETAINDB_API_KEY": { + "description": "RetainDB API key for persistent memory", + "prompt": "RetainDB API key", + "url": "https://retaindb.com", + "tools": ["retaindb_search"], + "password": True, + "category": "tool", + }, + "RETAINDB_BASE_URL": { + "description": "Base URL for self-hosted RetainDB instances (default: https://api.retaindb.com)", + "prompt": "RetainDB base URL", + "category": "tool", + "advanced": True, + }, + + # ── ByteRover ── + "BRV_API_KEY": { + "description": "ByteRover API key (optional, for cloud sync — local-first by default)", + "prompt": "ByteRover API key", + "url": "https://app.byterover.dev", + "tools": ["brv_query"], + "password": True, + "category": "tool", + }, + + # ── OpenViking ── + "OPENVIKING_API_KEY": { + "description": "OpenViking API key (leave blank for local dev mode)", + "prompt": "OpenViking API key", + "tools": ["viking_search"], + "password": True, + "category": "tool", + }, + "OPENVIKING_ENDPOINT": { + "description": "OpenViking server URL (default: http://127.0.0.1:1933)", + "prompt": "OpenViking endpoint", + "category": "tool", + "advanced": True, + }, + # ── Langfuse observability ── "HERMES_LANGFUSE_PUBLIC_KEY": { "description": "Langfuse project public key (pk-lf-...)", diff --git a/tests/hermes_cli/test_config.py b/tests/hermes_cli/test_config.py index 979733a4337..266ac26d1fa 100644 --- a/tests/hermes_cli/test_config.py +++ b/tests/hermes_cli/test_config.py @@ -700,6 +700,49 @@ class TestOptionalEnvVarsRegistry: assert "HERMES_MAX_ITERATIONS" not in OPTIONAL_ENV_VARS +class TestMemoryProviderEnvVarsRegistry: + """Every memory provider that reads an API key from the environment must + have that key catalogued in OPTIONAL_ENV_VARS so the dashboard Keys page + and `hermes setup` surface it (previously only Honcho was listed, leaving + Hindsight/Supermemory/Mem0/RetainDB/ByteRover/OpenViking invisible). + + This is a behavior contract, not a snapshot: it asserts each provider's + primary credential key is present, tool-categorised, and password-masked — + not a frozen count of entries. + """ + + # provider primary-credential env key -> the tool-call name it powers. + MEMORY_PROVIDER_KEYS = { + "HONCHO_API_KEY": "honcho_context", + "HINDSIGHT_API_KEY": "hindsight_recall", + "SUPERMEMORY_API_KEY": "supermemory_search", + "MEM0_API_KEY": "mem0_search", + "RETAINDB_API_KEY": "retaindb_search", + "BRV_API_KEY": "brv_query", + "OPENVIKING_API_KEY": "viking_search", + } + + def test_memory_provider_keys_are_catalogued(self): + from hermes_cli.config import OPTIONAL_ENV_VARS + missing = [k for k in self.MEMORY_PROVIDER_KEYS if k not in OPTIONAL_ENV_VARS] + assert not missing, f"memory provider keys missing from OPTIONAL_ENV_VARS: {missing}" + + def test_memory_provider_keys_are_tool_category(self): + from hermes_cli.config import OPTIONAL_ENV_VARS + for key in self.MEMORY_PROVIDER_KEYS: + assert OPTIONAL_ENV_VARS[key]["category"] == "tool", key + + def test_memory_provider_keys_are_password_masked(self): + from hermes_cli.config import OPTIONAL_ENV_VARS + for key in self.MEMORY_PROVIDER_KEYS: + assert OPTIONAL_ENV_VARS[key].get("password") is True, key + + def test_memory_provider_keys_advertise_their_tool(self): + from hermes_cli.config import OPTIONAL_ENV_VARS + for key, tool in self.MEMORY_PROVIDER_KEYS.items(): + assert tool in OPTIONAL_ENV_VARS[key].get("tools", []), key + + class TestConfigMigrationSecretPrompts: def test_required_secret_env_prompt_uses_masked_prompt(self, tmp_path, monkeypatch): from hermes_cli import config as cfg_mod