{"repo":"NousResearch/hermes-agent","public_id":"42130","display_label":"OpenRouter credential not detected or sent in requests","summary":"The members concern OpenRouter API credentials not being picked up by Hermes: one setup issue reports requests going out without an Authentication header despite a configured credential, and two fix-oriented PRs address detecting keys from ~/.hermes/.env and from the credential pool rather than only environment variables. Whether these changes address the reported issue is not established by the titles.","subsystem":"hermes_cli","counts":{"members":3,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":1,"merged_prs":1,"attempt_prs":1},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":42263,"merged_at":"2026-06-08T17:01:48+00:00"},"resolution":{"state":"resolved","by":"pr-42263"},"freshness":{"state":"verified","last_verified_at":"2026-06-08T17:01:48+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.6666666666666666},"fix_edges":[{"issue_number":42130,"pr_number":42263,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true,"pr_merged_at":"2026-06-08T17:01:48+00:00"}],"members":[{"kind":"issue","number":42130,"title":"[Setup]: OpenRouter credential configured but Hermes sends requests without Authentication header","url":null,"state":"closed"},{"kind":"pr","number":42183,"title":"fix(runtime-provider): check ~/.hermes/.env for OpenRouter API keys","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"f538470cf4afddb9ae6cc476c4f71b671f5a8420","number":0}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":42263,"title":"fix(auth): auto-detect OpenRouter credential from the pool, not just env","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["hermes_cli/auth.py","hermes_cli/dump.py","tests/hermes_cli/test_resolve_provider_openrouter_pool.py"],"fix_hunks":[{"file":"hermes_cli/auth.py","hunks":["@@ -1561,6 +1561,21 @@ def resolve_provider(\n     if has_usable_secret(os.getenv(\"OPENAI_API_KEY\")) or has_usable_secret(os.getenv(\"OPENROUTER_API_KEY\")):\n         return \"openrouter\"\n \n+    # Auto-detect an OpenRouter credential added via `hermes auth add openrouter`\n+    # (manual pool entry, no env var). Without this, a key that only lives in\n+    # the credential pool is invisible to auto-detection — the user sees\n+    # `hermes auth list` showing the credential while requests go out with no\n+    # Authorization header (\"HTTP 401: Missing Authentication header\"). The\n+    # env-var check above only covers keys exported as OPENROUTER_API_KEY /\n+    # OPENAI_API_KEY. See issue #42130.\n+    try:\n+        from agent.credential_pool import load_pool as _load_pool\n+\n+        if _load_pool(\"openrouter\").has_credentials():\n+            return \"openrouter\"\n+    except Exception as e:\n+        logger.debug(\"Could not check OpenRouter credential pool: %s\", e)\n+\n     # Auto-detect API-key providers by checking their env vars\n     for pid, pconfig in PROVIDER_REGISTRY.items():\n         if pconfig.auth_type != \"api_key\":"]},{"file":"hermes_cli/dump.py","hunks":["@@ -318,6 +318,17 @@ def run_dump(args):\n             display = _redact(val)\n         else:\n             display = \"set\" if val else \"not set\"\n+        # A credential added via `hermes auth add openrouter` lives in the\n+        # credential pool, not as an env var — surface it so the dump doesn't\n+        # misleadingly read \"not set\" while `hermes auth list` shows it (#42130).\n+        if not val and label == \"openrouter\":\n+            try:\n+                from agent.credential_pool import load_pool as _load_pool\n+\n+                if _load_pool(\"openrouter\").has_credentials():\n+                    display = \"set (auth pool)\"\n+            except Exception:\n+                pass\n         lines.append(f\"  {label:<20} {display}\")\n \n     # Features summary"]},{"file":"tests/hermes_cli/test_resolve_provider_openrouter_pool.py","hunks":["@@ -0,0 +1,76 @@\n+\"\"\"Regression tests for issue #42130.\n+\n+A credential added via `hermes auth add openrouter` lives in the credential\n+pool, NOT as an OPENROUTER_API_KEY env var. Before the fix, resolve_provider()\n+auto-detection only checked env vars, so such a credential was invisible:\n+the provider failed to resolve (AuthError) or resolved without a key, and\n+requests went out with no Authorization header — OpenRouter's\n+\"HTTP 401: Missing Authentication header\".\n+\n+These tests lock in that auto-detection consults the OpenRouter pool.\n+\"\"\"\n+\n+import uuid\n+\n+import pytest\n+\n+\n[redacted:email](autouse=True)\n+def _clean_inference_env(monkeypatch):\n+    \"\"\"Strip credential-shaped env vars so the pool is the only source.\"\"\"\n+    for key in (\n+        \"OPENROUTER_API_KEY\",\n+        \"OPENAI_API_KEY\",\n+        \"ANTHROPIC_API_KEY\",\n+        \"ANTHROPIC_TOKEN\",\n+        \"CLAUDE_CODE_OAUTH_TOKEN\",\n+        \"NOUS_API_KEY\",\n+        \"HERMES_INFERENCE_PROVIDER\",\n+    ):\n+        monkeypatch.delenv(key, raising=False)\n+\n+\n+def _seed_openrouter_pool(token: str = \"sk-or-FAKEKEY123\") -> None:\n+    \"\"\"Mimic `hermes auth add openrouter <token>` — a manual pool entry.\"\"\"\n+    from agent.credential_pool import (\n+        AUTH_TYPE_API_KEY,\n+        SOURCE_MANUAL,\n+        PooledCredential,\n+        load_pool,\n+    )\n+\n+    pool = load_pool(\"openrouter\")\n+    pool.add_entry(\n+        PooledCredential(\n+            provider=\"openrouter\",\n+            id=uuid.uuid4().hex[:6],\n+            label=\"api-key-1\",\n+            auth_type=AUTH_TYPE_API_KEY,\n+            priority=0,\n+            source=SOURCE_MANUAL,\n+            access_token=[redacted:secret-assignment]\n+            base_url=\"https://openrouter.ai/api/v1\",\n+        )\n+    )\n+\n+\n+def test_auto_detects_openrouter_from_pool(tmp_path, monkeypatch):\n+    \"\"\"With only a pool credential (no env var), auto-detection finds it.\"\"\"\n+    monkeypatch.setenv(\"HERMES_HOME\", str(tmp_path / \"hermes\"))\n+    (tmp_path / \"hermes\").mkdir(parents=True, exist_ok=True)\n+    _seed_openrouter_pool()\n+\n+    from hermes_cli.auth import resolve_provider\n+\n+    assert resolve_provider(\"auto\") == \"openrouter\"\n+\n+\n+def test_no_credentials_still_raises(tmp_path, monkeypatch):\n+    \"\"\"Empty pool + no env var must still fail to resolve — no false positive.\"\"\"\n+    monkeypatch.setenv(\"HERMES_HOME\", str(tmp_path / \"hermes\"))\n+    (tmp_path / \"hermes\").mkdir(parents=True, exist_ok=True)\n+\n+    from hermes_cli.auth import AuthError, resolve_provider\n+\n+    with pytest.raises(AuthError):\n+        resolve_provider(\"auto\")\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}