{"repo":"NousResearch/hermes-agent","public_id":"966","display_label":"ZAI and Kimi provider endpoint routing fixes","summary":"These issues and PRs address incorrect endpoint selection, ignored base URL configuration, and wrong billing paths for the ZAI/GLM and Kimi/Kimi-Coding providers across authentication, credential pools, and vision auxiliary clients. A recurring theme is hardcoded or incorrectly ordered endpoint probing causing users to be charged against metered quotas instead of subscription/coding-plan quotas.","subsystem":"agent","counts":{"members":31,"open_issues":4,"open_issues_distinct":4,"open_prs":8,"closed_issues":8,"merged_prs":1,"attempt_prs":18},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":9907},"resolution":{"state":"partially_resolved","by":"pr-9907"},"freshness":{"state":"stale","last_verified_at":"2026-04-14T23:26:01+00:00","is_stale":true},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.05324731182795698},"fix_edges":[{"issue_number":8461,"pr_number":9907,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":9888,"pr_number":9907,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true}],"members":[{"kind":"issue","number":966,"title":"[Feature]: Add Kimi Coding as a direct inference provider","url":null,"state":"closed"},{"kind":"issue","number":5561,"title":"kimi-coding credential pool seeds wrong base_url for sk-kimi- keys, causing 401 on first request","url":null,"state":"closed"},{"kind":"issue","number":5908,"title":"kimi-coding credential pool base_url not re-resolved from key prefix on load","url":null,"state":"open"},{"kind":"issue","number":9888,"title":"[Feature]: Glm-5v-turbo for GLM Coding Plan","url":null,"state":"closed"},{"kind":"issue","number":13935,"title":"[Bug/UX]: kimi-coding provider misleading — points to kimi.com/coding not platform.moonshot.ai","url":null,"state":"closed"},{"kind":"issue","number":18302,"title":"[Bug]: ZAI/GLM provider base_url causes 429/404 errors — config ignored by 5 code paths","url":null,"state":"open"},{"kind":"issue","number":42536,"title":"[bug] Z.AI/GLM provider endpoint probe order causes Coding Plan users to be charged against wallet balance instead of subscription quota","url":null,"state":"open"},{"kind":"issue","number":43617,"title":"[Bug]:   kimi-coding provider uses wrong endpoint + User-Agent for sk-kimi-* keys, breaking all API calls including vision","url":null,"state":"closed"},{"kind":"issue","number":55112,"title":"[Bug]: hardcoded zai vision base_urls (paas/v4) bypass the coding-plan endpoint → silent metered billing","url":null,"state":"open"},{"kind":"issue","number":58071,"title":"[Bug] zai provider ignores config.yaml base_url and always probes endpoints in hardcoded order — standard endpoint always wins over coding endpoint","url":null,"state":"closed"},{"kind":"issue","number":61563,"title":"zai manual-pool credentials always routed to metered /api/paas/v4 instead of /api/coding/paas/v4 (hermes auth add cannot override base_url)","url":null,"state":"closed"},{"kind":"issue","number":68239,"title":"hermes auth add stamps registry-default base_url for kimi-coding — sk-kimi- keys paired with api.moonshot.ai/v1, instant 401","url":null,"state":"closed"},{"kind":"pr","number":967,"title":"feat(providers): add Kimi Coding endpoint with auto-detection","url":null,"state":"closed","close_reason":{"category":"merged_via","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":5566,"title":"fix(credential_pool): use _resolve_kimi_base_url when seeding kimi-coding pool","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#5669","number":5669}],"category":"uncategorized","implication":"needs_llm"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":5669,"title":"fix(auth): kimi-coding pool base_url seeding + PKCE endpoint fallback","url":null,"state":"closed","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":9907,"title":"feat(zai): add GLM-5V-Turbo support for coding plan","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/auxiliary_client.py","hermes_cli/auth.py","hermes_cli/models.py"],"fix_hunks":[{"file":"agent/auxiliary_client.py","hunks":["@@ -112,6 +112,7 @@ def _normalize_aux_provider(provider: Optional[str]) -> str:\n # \"exotic provider\" branch checks this before falling back to the main model.\n _PROVIDER_VISION_MODELS: Dict[str, str] = {\n     \"xiaomi\": \"mimo-v2-omni\",\n+    \"zai\": \"glm-5v-turbo\",\n }\n \n # OpenRouter app attribution headers"]},{"file":"hermes_cli/auth.py","hunks":["@@ -383,13 +383,16 @@ def _resolve_api_key_provider_secret(\n # Z.AI has separate billing for general vs coding plans, and global vs China\n # endpoints.  A key that works on one may return \"Insufficient balance\" on\n # another.  We probe at setup time and store the working endpoint.\n+# Each entry lists candidate models to try in order — newer coding plan accounts\n+# may only have access to recent models (glm-5.1, glm-5v-turbo) while older\n+# ones still use glm-4.7.\n \n ZAI_ENDPOINTS = [\n-    # (id, base_url, default_model, label)\n-    (\"global\",        \"https://api.z.ai/api/paas/v4\",        \"glm-5\",   \"Global\"),\n-    (\"cn\",            \"https://open.bigmodel.cn/api/paas/v4\", \"glm-5\",   \"China\"),\n-    (\"coding-global\", \"https://api.z.ai/api/coding/paas/v4\",  \"glm-4.7\", \"Global (Coding Plan)\"),\n-    (\"coding-cn\",     \"https://open.bigmodel.cn/api/coding/paas/v4\", \"glm-4.7\", \"China (Coding Plan)\"),\n+    # (id, base_url, probe_models, label)\n+    (\"global\",        \"https://api.z.ai/api/paas/v4\",        [\"glm-5\"],   \"Global\"),\n+    (\"cn\",            \"https://open.bigmodel.cn/api/paas/v4\", [\"glm-5\"],   \"China\"),\n+    (\"coding-global\", \"https://api.z.ai/api/coding/paas/v4\",  [\"glm-5.1\", \"glm-5v-turbo\", \"glm-4.7\"], \"Global (Coding Plan)\"),\n+    (\"coding-cn\",     \"https://open.bigmodel.cn/api/coding/paas/v4\", [\"glm-5.1\", \"glm-5v-turbo\", \"glm-4.7\"], \"China (Coding Plan)\"),\n ]\n \n ","@@ -397,35 +400,37 @@ def detect_zai_endpoint(api_key: str, timeout: float = 8.0) -> Optional[Dict[str\n     \"\"\"Probe z.ai endpoints to find one that accepts this API key.\n \n     Returns {\"id\": ..., \"base_url\": ..., \"model\": ..., \"label\": ...} for the\n-    first working endpoint, or None if all fail.\n+    first working endpoint, or None if all fail.  For endpoints with multiple\n+    candidate models, tries each in order and returns the first that succeeds.\n     \"\"\"\n-    for ep_id, base_url, model, label in ZAI_ENDPOINTS:\n-        try:\n-            resp = httpx.post(\n-                f\"{base_url}/chat/completions\",\n-                headers={\n-                    \"Authorization\": f\"Bearer {api_key}\",\n-                    \"Content-Type\": \"application/json\",\n-                },\n-                json={\n-                    \"model\": model,\n-                    \"stream\": False,\n-                    \"max_tokens\": 1,\n-                    \"messages\": [{\"role\": \"user\", \"content\": \"ping\"}],\n-                },\n-                timeout=timeout,\n-            )\n-            if resp.status_code == 200:\n-                logger.debug(\"Z.AI endpoint probe: %s (%s) OK\", ep_id, base_url)\n-                return {\n-                    \"id\": ep_id,\n-                    \"base_url\": base_url,\n-                    \"model\": model,\n-                    \"label\": label,\n-                }\n-            logger.debug(\"Z.AI endpoint probe: %s returned %s\", ep_id, resp.status_code)\n-        except Exception as exc:\n-            logger.debug(\"Z.AI endpoint probe: %s failed: %s\", ep_id, exc)\n+    for ep_id, base_url, probe_models, label in ZAI_ENDPOINTS:\n+        for model in probe_models:\n+            try:\n+                resp = httpx.post(\n+                    f\"{base_url}/chat/completions\",\n+                    headers={\n+                        \"Authorization\": f\"Bearer {api_key}\",\n+                        \"Content-Type\": \"application/json\",\n+                    },\n+                    json={\n+                        \"model\": model,\n+                        \"stream\": False,\n+                        \"max_tokens\": 1,\n+                        \"messages\": [{\"role\": \"user\", \"content\": \"ping\"}],\n+                    },\n+                    timeout=timeout,\n+                )\n+                if resp.status_code == 200:\n+                    logger.debug(\"Z.AI endpoint probe: %s (%s) model=%s OK\", ep_id, base_url, model)\n+                    return {\n+                        \"id\": ep_id,\n+                        \"base_url\": base_url,\n+                        \"model\": model,\n+                        \"label\": label,\n+                    }\n+                logger.debug(\"Z.AI endpoint probe: %s model=%s returned %s\", ep_id, model, resp.status_code)\n+            except Exception as exc:\n+                logger.debug(\"Z.AI endpoint probe: %s model=%s failed: %s\", ep_id, model, exc)\n     return None\n \n "]},{"file":"hermes_cli/models.py","hunks":["@@ -44,6 +44,7 @@\n     (\"minimax/minimax-m2.7\",            \"\"),\n     (\"minimax/minimax-m2.5\",            \"\"),\n     (\"z-ai/glm-5.1\",                    \"\"),\n+    (\"z-ai/glm-5v-turbo\",               \"\"),\n     (\"z-ai/glm-5-turbo\",                \"\"),\n     (\"moonshotai/kimi-k2.5\",            \"\"),\n     (\"x-ai/grok-4.20\",                  \"\"),","@@ -89,6 +90,7 @@ def _codex_curated_models() -> list[str]:\n         \"minimax/minimax-m2.7\",\n         \"minimax/minimax-m2.5\",\n         \"z-ai/glm-5.1\",\n+        \"z-ai/glm-5v-turbo\",\n         \"z-ai/glm-5-turbo\",\n         \"moonshotai/kimi-k2.5\",\n         \"x-ai/grok-4.20-beta\",","@@ -134,6 +136,7 @@ def _codex_curated_models() -> list[str]:\n     \"zai\": [\n         \"glm-5.1\",\n         \"glm-5\",\n+        \"glm-5v-turbo\",\n         \"glm-5-turbo\",\n         \"glm-4.7\",\n         \"glm-4.5\",\n"]}]},{"kind":"pr","number":13954,"title":"fix(setup): clarify Moonshot/Kimi provider labels and accept MOONSHOT_API_KEY (#13935)","url":null,"state":"closed","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":13993,"title":"fix(providers): add moonshotai provider, disambiguate from kimi-for-coding","url":null,"state":"open","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false},{"kind":"pr","number":18694,"title":"fix(auth): re-resolve Kimi pool base URLs","url":null,"state":"open","close_reason":{"refs":[{"kind":"pr","value":"#83537","number":83537},{"kind":"pr","value":"#5908","number":5908},{"kind":"pr","value":"#83537","number":83537},{"kind":"pr","value":"#83537","number":83537}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":37816,"title":"Respect configured ZAI base URL","url":null,"state":"open","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false},{"kind":"pr","number":42547,"title":"fix(zai): probe Coding Plan endpoints before generic to prevent silent wallet drain","url":null,"state":"open","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false},{"kind":"pr","number":42678,"title":"fix(auth): reorder Z.AI endpoint probe to try coding plan first (#42536)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#42547","number":42547},{"kind":"pr","value":"#42536","number":42536}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":43629,"title":"fix(provider): use api.kimi.com/coding endpoint for kimi-coding provider","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#43617","number":43617}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":43813,"title":"fix(provider): update kimi-coding endpoint and User-Agent (#43617)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#43629","number":43629},{"kind":"pr","value":"#43617","number":43617}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":48509,"title":"fix(auxiliary_client): resolve API keys for inferred custom endpoints","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"a8841e2a6877f7b5e7b4b6ab0122ef2fde229fe2","number":0}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":55116,"title":"fix(auxiliary): prepend zai coding-plan vision endpoint before metered paas/v4 (fixes #55112)","url":null,"state":"open","close_reason":{"refs":[{"kind":"pr","value":"#62467","number":62467}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":55156,"title":"fix(auxiliary): try zai coding-plan vision endpoint before metered fallbacks (#55112)","url":null,"state":"open","close_reason":{"refs":[{"kind":"pr","value":"#55116","number":55116},{"kind":"pr","value":"#55112","number":55112},{"kind":"pr","value":"#55116","number":55116},{"kind":"pr","value":"#55112","number":55112}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":58088,"title":"fix(zai): honor configured base url","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"23d3edac4e","number":0}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":61575,"title":"fix(agent): route Z.AI manual-pool entries through _resolve_zai_base_url","url":null,"state":"closed","close_reason":{"category":"superseded_by_pr","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":83537,"title":"fix(auth): re-resolve stale kimi pool base_url on load (#5908)","url":null,"state":"open","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false},{"kind":"pr","number":114111,"title":"fix(auth): route Kimi Code credentials by key","url":null,"state":"open","close_reason":{"category":"silent_close","implication":"active"},"merged":false}],"trust":{"default_tier":"verified","includes_suggested":false}}