{"repo":"NousResearch/hermes-agent","public_id":"17945","display_label":"delegate_task 404 errors and provider routing fixes","summary":"Multiple bug reports and fixes addressing HTTP 404 errors from the delegate_task tool, particularly with DeepSeek and other providers. Fixes cover provider auto-detection, API mode handling for direct endpoints, and URL classification for delegation routing.","subsystem":"agent","counts":{"members":6,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":2,"merged_prs":1,"attempt_prs":3},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":26824},"resolution":{"state":"unresolved","by":null},"freshness":{"state":"verified","last_verified_at":"2026-05-16T08:00:27+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.29066666666666663},"fix_edges":[{"issue_number":10213,"pr_number":26824,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":26210,"pr_number":26824,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false}],"members":[{"kind":"issue","number":17945,"title":"[Bug] delegate_task tool returning HTTP 404","url":null,"state":"closed"},{"kind":"issue","number":26210,"title":"delegate_task returns HTTP 404 with DeepSeek provider","url":null,"state":"closed"},{"kind":"pr","number":26331,"title":"fix(delegate): honor custom provider api mode for direct endpoints","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#10273","number":10273},{"kind":"pr","value":"#5697","number":5697},{"kind":"pr","value":"#11756","number":11756}],"category":"uncategorized","implication":"needs_llm"},"merged":false},{"kind":"pr","number":26376,"title":"fix(delegate): honor api mode for direct endpoints","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#26210","number":26210},{"kind":"pr","value":"#26331","number":26331}],"category":"uncategorized","implication":"needs_llm"},"merged":false},{"kind":"pr","number":26824,"title":"fix(delegation): honor api_mode + auto-detect anthropic_messages URLs","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["hermes_cli/config.py","tests/tools/test_delegate.py","tools/delegate_tool.py"],"fix_hunks":[{"file":"hermes_cli/config.py","hunks":["@@ -1146,6 +1146,10 @@ def _ensure_hermes_home_managed(home: Path):\n         \"provider\": \"\",    # e.g. \"openrouter\" (empty = inherit parent provider + credentials)\n         \"base_url\": \"\",    # direct OpenAI-compatible endpoint for subagents\n         \"api_key\": \"\",     # API key for delegation.base_url (falls back to OPENAI_API_KEY)\n+        \"api_mode\": \"\",    # wire protocol for delegation.base_url: \"chat_completions\",\n+                           # \"codex_responses\", or \"anthropic_messages\". Empty = auto-detect\n+                           # from URL (e.g. /anthropic suffix → anthropic_messages). Set this\n+                           # explicitly for non-standard endpoints the heuristic can't detect.\n         # When delegate_task narrows child toolsets explicitly, preserve any\n         # MCP toolsets the parent already has enabled. On by default so\n         # narrowing (e.g. toolsets=[\"web\",\"browser\"]) expresses \"I want these"]},{"file":"tests/tools/test_delegate.py","hunks":["@@ -890,6 +890,63 @@ def test_direct_endpoint_uses_configured_base_url_and_api_key(self):\n         self.assertEqual(creds[\"api_key\"], \"local-key\")\n         self.assertEqual(creds[\"api_mode\"], \"chat_completions\")\n \n+    def test_direct_endpoint_auto_detects_anthropic_messages_suffix(self):\n+        # Issue #10213: Azure AI Foundry exposes Anthropic-compatible models at\n+        # a /anthropic URL suffix. Subagents must pick anthropic_messages\n+        # automatically, matching the main agent's runtime resolver.\n+        parent = _make_mock_parent(depth=0)\n+        cfg = {\n+            \"model\": \"claude-opus-4-6\",\n+            \"provider\": \"custom\",\n+            \"base_url\": \"https://myfoundry.services.ai.azure.com/anthropic\",\n+            \"api_key\": \"foundry-key\",\n+        }\n+        creds = _resolve_delegation_credentials(cfg, parent)\n+        self.assertEqual(creds[\"provider\"], \"custom\")\n+        self.assertEqual(creds[\"base_url\"], \"https://myfoundry.services.ai.azure.com/anthropic\")\n+        self.assertEqual(creds[\"api_key\"], \"foundry-key\")\n+        self.assertEqual(creds[\"api_mode\"], \"anthropic_messages\")\n+\n+    def test_direct_endpoint_honors_explicit_api_mode(self):\n+        # When delegation.api_mode is set explicitly, it overrides URL-based\n+        # detection so users can force a transport on non-standard endpoints.\n+        parent = _make_mock_parent(depth=0)\n+        cfg = {\n+            \"model\": \"claude-opus-4-6\",\n+            \"provider\": \"custom\",\n+            \"base_url\": \"https://proxy.example.com/v1\",\n+            \"api_key\": \"proxy-key\",\n+            \"api_mode\": \"anthropic_messages\",\n+        }\n+        creds = _resolve_delegation_credentials(cfg, parent)\n+        self.assertEqual(creds[\"api_mode\"], \"anthropic_messages\")\n+\n+    def test_direct_endpoint_explicit_api_mode_overrides_url_detection(self):\n+        # Explicit api_mode in config always wins over auto-detection.\n+        parent = _make_mock_parent(depth=0)\n+        cfg = {\n+            \"model\": \"claude-opus-4-6\",\n+            \"provider\": \"custom\",\n+            \"base_url\": \"https://myfoundry.services.ai.azure.com/anthropic\",\n+            \"api_key\": \"foundry-key\",\n+            \"api_mode\": \"chat_completions\",\n+        }\n+        creds = _resolve_delegation_credentials(cfg, parent)\n+        self.assertEqual(creds[\"api_mode\"], \"chat_completions\")\n+\n+    def test_direct_endpoint_invalid_api_mode_falls_back_to_detection(self):\n+        # An invalid api_mode string must not break detection; fall back to URL heuristic.\n+        parent = _make_mock_parent(depth=0)\n+        cfg = {\n+            \"model\": \"claude-opus-4-6\",\n+            \"provider\": \"custom\",\n+            \"base_url\": \"https://myfoundry.services.ai.azure.com/anthropic\",\n+            \"api_key\": \"foundry-key\",\n+            \"api_mode\": \"garbage\",\n+        }\n+        creds = _resolve_delegation_credentials(cfg, parent)\n+        self.assertEqual(creds[\"api_mode\"], \"anthropic_messages\")\n+\n     def test_direct_endpoint_returns_none_api_key_when_not_configured(self):\n         # When base_url is set without api_key, api_key should be None so\n         # _build_child_agent inherits the parent's key (effective_api_key = override or parent)."]},{"file":"tools/delegate_tool.py","hunks":["@@ -2362,6 +2362,7 @@ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict:\n     configured_provider = str(cfg.get(\"provider\") or \"\").strip() or None\n     configured_base_url = str(cfg.get(\"base_url\") or \"\").strip() or None\n     configured_api_key = [redacted:secret-assignment]\"api_key\") or \"\").strip() or None\n+    configured_api_mode = str(cfg.get(\"api_mode\") or \"\").strip().lower() or None\n \n     if configured_base_url:\n         # When delegation.api_key is not set, return None so _build_child_agent","@@ -2372,9 +2373,17 @@ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict:\n         # callers to duplicate the key under delegation.api_key.\n         api_key = [redacted:secret-assignment]  # None → inherited from parent in _build_child_agent\n \n+        # Use the shared URL-based api_mode detector (same path the main agent's\n+        # runtime resolver uses) so Anthropic-compatible direct endpoints with a\n+        # /anthropic suffix — Azure AI Foundry, MiniMax, Zhipu GLM, LiteLLM\n+        # proxies — pick the right transport automatically. Without this,\n+        # subagents would default to chat_completions and hit 404s on endpoints\n+        # that only speak the Anthropic Messages protocol. Fixes #10213.\n+        from hermes_cli.runtime_provider import _detect_api_mode_for_url\n+\n         base_lower = configured_base_url.lower()\n         provider = \"custom\"\n-        api_mode = \"chat_completions\"\n+        api_mode = _detect_api_mode_for_url(configured_base_url) or \"chat_completions\"\n         if (\n             base_url_hostname(configured_base_url) == \"chatgpt.com\"\n             and \"/backend-api/codex\" in base_lower","@@ -2388,6 +2397,11 @@ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict:\n             provider = \"custom\"\n             api_mode = \"anthropic_messages\"\n \n+        # Explicit delegation.api_mode in config always wins. Lets users force\n+        # a transport for non-standard endpoints the URL heuristic can't detect.\n+        if configured_api_mode in {\"chat_completions\", \"codex_responses\", \"anthropic_messages\"}:\n+            api_mode = configured_api_mode\n+\n         return {\n             \"model\": configured_model,\n             \"provider\": provider,\n"]}]},{"kind":"pr","number":33324,"title":"fix(tools): recognize api.deepseek.com as deepseek provider in delegate_task","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"c445f48b78ad9dfb142d2337b51f227bde66cc84","number":0},{"kind":"pr","value":"#26824","number":26824}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false}],"trust":{"default_tier":"verified","includes_suggested":false}}