{"repo":"NousResearch/hermes-agent","public_id":"5089","display_label":"Custom provider context_length not respected in compression paths","summary":"A cluster of bug reports and fixes centered on custom provider context_length values being ignored across compression feasibility checks, auxiliary compression, context hygiene, and related gateway/agent paths. Work involves propagating custom_providers configuration through the relevant code paths so context-length detection honors user-defined values.","subsystem":"agent","counts":{"members":61,"open_issues":2,"open_issues_distinct":2,"open_prs":3,"closed_issues":21,"merged_prs":7,"attempt_prs":31},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":8700},"resolution":{"state":"partially_resolved","by":"pr-8700,pr-15844,pr-20218,pr-59522,pr-73578"},"freshness":{"state":"verified","last_verified_at":"2026-10-06T12:50:10+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.034207650273224026},"fix_edges":[{"issue_number":5089,"pr_number":15844,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":8499,"pr_number":8700,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":12977,"pr_number":20218,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":12977,"pr_number":25494,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":15779,"pr_number":15844,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":15779,"pr_number":133822,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":26548,"pr_number":15844,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":30178,"pr_number":73578,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":59314,"pr_number":59522,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true}],"members":[{"kind":"issue","number":5089,"title":"Bug: Status banner fails to detect context_length from custom_providers","url":null,"state":"closed"},{"kind":"issue","number":8499,"title":"[Bug] `auxiliary.compression.context_length` config ignored in compression model feasibility check","url":null,"state":"closed"},{"kind":"issue","number":8548,"title":"Bug: compression model context length not read from custom_providers","url":null,"state":"closed"},{"kind":"issue","number":8550,"title":"Bug: compression model context length not read from custom_providers","url":null,"state":"closed"},{"kind":"issue","number":8785,"title":"Bug: compression runtime context_length overrides are not applied consistently","url":null,"state":"closed"},{"kind":"issue","number":10157,"title":"Feature: Warn on context_length mismatch after /model --global switch","url":null,"state":"open"},{"kind":"issue","number":12156,"title":"Telegram gateway ignores context_length from custom_providers entries","url":null,"state":"closed"},{"kind":"issue","number":12977,"title":"[Bug]: custom_providers.models.context_length not propagated to auxiliary compression feasibility check","url":null,"state":"closed"},{"kind":"issue","number":13807,"title":"auxiliary compression model does not read context_length from providers config","url":null,"state":"closed"},{"kind":"issue","number":15563,"title":"get_model_context_length step 2 short-circuits to 128K default; resolve_display_context_length skips config_context_length","url":null,"state":"open"},{"kind":"issue","number":15779,"title":"Bug: /model switch to named custom provider ignores custom_providers model context_length","url":null,"state":"closed"},{"kind":"issue","number":17690,"title":"but the main model's compression threshold was","url":null,"state":"closed"},{"kind":"issue","number":19539,"title":"Compression feasibility check ignores custom provider context_length for auxiliary compression","url":null,"state":"closed"},{"kind":"issue","number":20608,"title":"_check_compression_model_feasibility doesn't pass custom_providers to get_model_context_length","url":null,"state":"closed"},{"kind":"issue","number":21919,"title":"[Bug]: Auxiliary compression model does not inherit context_length from custom_providers","url":null,"state":"closed"},{"kind":"issue","number":21947,"title":"[Bug]: Auxiliary compression model does not inherit context_length from custom_providers","url":null,"state":"closed"},{"kind":"issue","number":23779,"title":"compression feasibility check ignores custom_providers context_length, causes false 256K warning","url":null,"state":"closed"},{"kind":"issue","number":24072,"title":"# Bug Report: `model.context_length` persists across `/model` provider switches","url":null,"state":"closed"},{"kind":"issue","number":26548,"title":"[Bug]: `auxiliary.*` context_length ignores `custom_providers[].models[].context_length`","url":null,"state":"closed"},{"kind":"issue","number":30178,"title":"[Bug]: LM Studio custom_providers per-model context_length broken in 0.14.0 — regressed to 64K","url":null,"state":"closed"},{"kind":"issue","number":39736,"title":"[Bug]: Compression model context length does not respect custom provider context length","url":null,"state":"closed"},{"kind":"issue","number":59314,"title":"fix: resolve_display_context_length ignores custom_providers per-model context_length","url":null,"state":"closed"},{"kind":"issue","number":63032,"title":"[Bug] /model switch does not rebase context_length — uses old model's budget","url":null,"state":"closed"},{"kind":"pr","number":8240,"title":"fix(gateway): read custom provider context length in status","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"3ccf723bf999d02698e44a30e1d6a9a90d7713f7","number":0},{"kind":"pr","value":"#5089","number":5089},{"kind":"pr","value":"#15844","number":15844}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":8511,"title":"fix(compression): pass configured context_length to feasibility check","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#8700","number":8700}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":8667,"title":"fix(compression): honor auxiliary context_length override","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#8700","number":8700},{"kind":"pr","value":"#8511","number":8511}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":8700,"title":"fix(compression): pass configured context_length to feasibility check","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["run_agent.py","tests/run_agent/test_compression_feasibility.py"]},{"kind":"pr","number":8721,"title":"fix(agent): honor custom provider context in compression feasibility check","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#17460","number":17460}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":8786,"title":"fix: unify context length overrides for compression runtimes","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"125de02056eab84362fc91f57bd7041a19860b22","number":0},{"kind":"commit","value":"46451528a50ad1adff2591650e9f0ccda561cd26","number":0}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":10366,"title":"fix(model): warn on stale context_length after /model --global","url":null,"state":"open","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false},{"kind":"pr","number":13540,"title":"fix(agent): propagate custom provider context_length to compression feasibility check","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"7becb19ea00c13bdff6f78b71aa3ddfb0bdb5378","number":0}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":13813,"title":"fix(run_agent): read context_length from providers/custom_providers for compression model","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"7becb19ea00c13bdff6f78b71aa3ddfb0bdb5378","number":0}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":14119,"title":"fix(agent): honor provider context for aux compression","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"d682f320b35a13084371a541a835e1d988c982b8","number":0}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":14152,"title":"fix(agent): propagate custom provider context to compression checks","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"7becb19ea00c13bdff6f78b71aa3ddfb0bdb5378","number":0},{"kind":"pr","value":"#25494","number":25494}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":14382,"title":"fix(gateway): read nested per-model context_length in session info banner","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"125de02056eab84362fc91f57bd7041a19860b22","number":0},{"kind":"pr","value":"#15844","number":15844}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":15397,"title":"fix: compression model should read context_length from custom_providers","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#13813","number":13813},{"kind":"pr","value":"#13807","number":13807},{"kind":"pr","value":"#12977","number":12977}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":15732,"title":"fix(context-length): fall through to catalog when endpoint probe misses; pass config_context_length to display resolver","url":null,"state":"closed","close_reason":{"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":15787,"title":"fix(model-switch): honor custom_providers per-model context_length on /model switch (#15779)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"125de0205","number":0},{"kind":"pr","value":"#15844","number":15844},{"kind":"pr","value":"#15779","number":15779},{"kind":"pr","value":"#15844","number":15844},{"kind":"pr","value":"#15844","number":15844}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":15844,"title":"fix(context): honor custom_providers context_length on /model switch + bump probe tier to 256K","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/model_metadata.py","gateway/run.py","hermes_cli/config.py","hermes_cli/model_switch.py","run_agent.py","tests/agent/test_model_metadata.py","tests/gateway/test_session_info.py","tests/hermes_cli/test_custom_provider_context_length.py","tests/hermes_cli/test_model_switch_context_display.py"],"fix_hunks":[{"file":"agent/model_metadata.py","hunks":["@@ -106,9 +106,11 @@ def _strip_provider_prefix(model: str) -> str:\n _ENDPOINT_MODEL_CACHE_TTL = 300\n \n # Descending tiers for context length probing when the model is unknown.\n-# We start at 128K (a safe default for most modern models) and step down\n-# on context-length errors until one works.\n+# We start at 256K (covers GPT-5.x, many current large-context models) and\n+# step down on context-length errors until one works.  Tier[0] is also the\n+# default fallback when no detection method succeeds.\n CONTEXT_PROBE_TIERS = [\n+    256_000,\n     128_000,\n     64_000,\n     32_000,","@@ -1193,6 +1195,7 @@ def get_model_context_length(\n     api_key: str = \"\",\n     config_context_length: int | None = None,\n     provider: str = \"\",\n+    custom_providers: list | None = None,\n ) -> int:\n     \"\"\"Get the context length for a model.\n ","@@ -1213,6 +1216,23 @@ def get_model_context_length(\n     if config_context_length is not None and isinstance(config_context_length, int) and config_context_length > 0:\n         return config_context_length\n \n+    # 0b. custom_providers per-model override — check before any probe.\n+    # This closes the gap where /model switch and display paths used to fall\n+    # back to 128K despite the user having a per-model context_length set.\n+    # See #15779.\n+    if custom_providers and base_url and model:\n+        try:\n+            from hermes_cli.config import get_custom_provider_context_length\n+            cp_ctx = get_custom_provider_context_length(\n+                model=model,\n+                base_url=base_url,\n+                custom_providers=custom_providers,\n+            )\n+            if cp_ctx:\n+                return cp_ctx\n+        except Exception:\n+            pass  # fall through to probing\n+\n     # Normalise provider-prefixed model names (e.g. \"local:model-name\" →\n     # \"model-name\") so cache lookups and server queries use the bare ID that\n     # local servers actually know about.  Ollama \"model:tag\" colons are preserved.","@@ -1352,7 +1372,7 @@ def get_model_context_length(\n     # 6. OpenRouter live API metadata (provider-unaware fallback)\n     metadata = fetch_model_metadata()\n     if model in metadata:\n-        return metadata[model].get(\"context_length\", 128000)\n+        return metadata[model].get(\"context_length\", DEFAULT_FALLBACK_CONTEXT)\n \n     # 8. Hardcoded defaults (fuzzy match — longest key first for specificity)\n     # Only check `default_model in model` (is the key a substring of the input)."]},{"file":"gateway/run.py","hunks":["@@ -4891,6 +4891,7 @@ def _format_session_info(self) -> str:\n         provider = None\n         base_url = None\n         api_key = None\n+        custom_provs = None\n \n         try:\n             cfg_path = _hermes_home / \"config.yaml\"","@@ -4908,6 +4909,11 @@ def _format_session_info(self) -> str:\n                             pass\n                     provider = model_cfg.get(\"provider\") or None\n                     base_url = model_cfg.get(\"base_url\") or None\n+                try:\n+                    from hermes_cli.config import get_compatible_custom_providers\n+                    custom_provs = get_compatible_custom_providers(data)\n+                except Exception:\n+                    custom_provs = data.get(\"custom_providers\")\n         except Exception:\n             pass\n ","@@ -4926,6 +4932,7 @@ def _format_session_info(self) -> str:\n             api_key=[redacted:secret-assignment] or \"\",\n             config_context_length=config_context_length,\n             provider=provider or \"\",\n+            custom_providers=custom_provs,\n         )\n \n         # Format context source hint","@@ -5601,6 +5608,7 @@ async def _on_model_selected(\n                             base_url=result.base_url or current_base_url or \"\",\n                             api_key=[redacted:secret-assignment] or current_api_key or \"\",\n                             model_info=mi,\n+                            custom_providers=custom_provs,\n                         )\n                         if ctx:\n                             lines.append(f\"Context: {ctx:,} tokens\")","@@ -5748,6 +5756,7 @@ async def _on_model_selected(\n             base_url=result.base_url or current_base_url or \"\",\n             api_key=[redacted:secret-assignment] or current_api_key or \"\",\n             model_info=mi,\n+            custom_providers=custom_provs,\n         )\n         if ctx:\n             lines.append(f\"Context: {ctx:,} tokens\")"]},{"file":"hermes_cli/config.py","hunks":["@@ -2206,6 +2206,71 @@ def _append_if_new(entry: Optional[Dict[str, Any]]) -> None:\n     return compatible\n \n \n+def get_custom_provider_context_length(\n+    model: str,\n+    base_url: str,\n+    custom_providers: Optional[List[Dict[str, Any]]] = None,\n+    config: Optional[Dict[str, Any]] = None,\n+) -> Optional[int]:\n+    \"\"\"Look up a per-model ``context_length`` override from ``custom_providers``.\n+\n+    Matches any entry whose ``base_url`` equals ``base_url`` (trailing-slash\n+    insensitive) and returns ``custom_providers[i].models.<model>.context_length``\n+    if present and valid.  Returns ``None`` when no override applies.\n+\n+    This is the single source of truth for custom-provider context overrides,\n+    used by:\n+      * ``AIAgent.__init__`` (startup resolution)\n+      * ``AIAgent.switch_model`` (mid-session ``/model`` switch)\n+      * ``hermes_cli.model_switch.resolve_display_context_length`` (``/model`` confirmation display)\n+      * ``gateway.run._format_session_info`` (``/info`` display)\n+      * ``agent.model_metadata.get_model_context_length`` (when custom_providers is threaded through)\n+\n+    Before this helper existed, the lookup was duplicated in ``run_agent.py``'s\n+    startup path only; every other path (notably ``/model`` switch) fell back\n+    to the 128K default.  See #15779.\n+    \"\"\"\n+    if not model or not base_url:\n+        return None\n+    if custom_providers is None:\n+        try:\n+            custom_providers = get_compatible_custom_providers(config)\n+        except Exception:\n+            if config is None:\n+                return None\n+            raw = config.get(\"custom_providers\")\n+            custom_providers = raw if isinstance(raw, list) else []\n+    if not isinstance(custom_providers, list):\n+        return None\n+\n+    target_url = (base_url or \"\").rstrip(\"/\")\n+    if not target_url:\n+        return None\n+\n+    for entry in custom_providers:\n+        if not isinstance(entry, dict):\n+            continue\n+        entry_url = (entry.get(\"base_url\") or \"\").rstrip(\"/\")\n+        if not entry_url or entry_url != target_url:\n+            continue\n+        models = entry.get(\"models\")\n+        if not isinstance(models, dict):\n+            continue\n+        model_cfg = models.get(model)\n+        if not isinstance(model_cfg, dict):\n+            continue\n+        raw_ctx = model_cfg.get(\"context_length\")\n+        if raw_ctx is None:\n+            continue\n+        try:\n+            ctx = int(raw_ctx)\n+        except (TypeError, ValueError):\n+            continue\n+        if ctx > 0:\n+            return ctx\n+    return None\n+\n+\n def check_config_version() -> Tuple[int, int]:\n     \"\"\"\n     Check config version."]},{"file":"hermes_cli/model_switch.py","hunks":["@@ -533,6 +533,7 @@ def resolve_display_context_length(\n     base_url: str = \"\",\n     api_key: str = \"\",\n     model_info: Optional[ModelInfo] = None,\n+    custom_providers: list | None = None,\n ) -> Optional[int]:\n     \"\"\"Resolve the context length to show in /model output.\n ","@@ -543,6 +544,11 @@ def resolve_display_context_length(\n     about Codex OAuth, Copilot, Nous, and falls back to models.dev for the\n     rest.\n \n+    When ``custom_providers`` is provided, per-model ``context_length``\n+    overrides from ``custom_providers[].models.<id>.context_length`` are\n+    honored — this closes #15779 where ``/model`` switch ignored user-set\n+    overrides.\n+\n     Prefer the provider-aware value; fall back to ``model_info.context_window``\n     only if the resolver returns nothing.\n     \"\"\"","@@ -553,6 +559,7 @@ def resolve_display_context_length(\n             base_url=base_url or \"\",\n             api_key=[redacted:secret-assignment] or \"\",\n             provider=provider or None,\n+            custom_providers=custom_providers,\n         )\n         if ctx:\n             return int(ctx)"]},{"file":"run_agent.py","hunks":["@@ -1765,43 +1765,64 @@ def __init__(\n         # Store for reuse in switch_model (so config override persists across model switches)\n         self._config_context_length = _config_context_length\n \n+        # Resolve custom_providers list once for reuse below (startup\n+        # context-length override and plugin context-engine init).\n+        try:\n+            from hermes_cli.config import get_compatible_custom_providers\n+            _custom_providers = get_compatible_custom_providers(_agent_cfg)\n+        except Exception:\n+            _custom_providers = _agent_cfg.get(\"custom_providers\")\n+            if not isinstance(_custom_providers, list):\n+                _custom_providers = []\n+\n         # Check custom_providers per-model context_length\n-        if _config_context_length is None:\n+        if _config_context_length is None and _custom_providers:\n             try:\n-                from hermes_cli.config import get_compatible_custom_providers\n-                _custom_providers = get_compatible_custom_providers(_agent_cfg)\n+                from hermes_cli.config import get_custom_provider_context_length\n+                _cp_ctx_resolved = get_custom_provider_context_length(\n+                    model=self.model,\n+                    base_url=self.base_url,\n+                    custom_providers=_custom_providers,\n+                )\n+                if _cp_ctx_resolved:\n+                    _config_context_length = int(_cp_ctx_resolved)\n             except Exception:\n-                _custom_providers = _agent_cfg.get(\"custom_providers\")\n-                if not isinstance(_custom_providers, list):\n-                    _custom_providers = []\n-            for _cp_entry in _custom_providers:\n-                if not isinstance(_cp_entry, dict):\n-                    continue\n-                _cp_url = (_cp_entry.get(\"base_url\") or \"\").rstrip(\"/\")\n-                if _cp_url and _cp_url == self.base_url.rstrip(\"/\"):\n-                    _cp_models = _cp_entry.get(\"models\", {})\n-                    if isinstance(_cp_models, dict):\n-                        _cp_model_cfg = _cp_models.get(self.model, {})\n-                        if isinstance(_cp_model_cfg, dict):\n-                            _cp_ctx = _cp_model_cfg.get(\"context_length\")\n-                            if _cp_ctx is not None:\n-                                try:\n-                                    _config_context_length = int(_cp_ctx)\n-                                except (TypeError, ValueError):\n-                                    logger.warning(\n-                                        \"Invalid context_length for model %r in \"\n-                                        \"custom_providers: %r — must be a plain \"\n-                                        \"integer (e.g. 256000, not '256K'). \"\n-                                        \"Falling back to auto-detection.\",\n-                                        self.model, _cp_ctx,\n-                                    )\n-                                    print(\n-                                        f\"\\n⚠ Invalid context_length for model {self.model!r} in custom_providers: {_cp_ctx!r}\\n\"\n-                                        f\"  Must be a plain integer (e.g. 256000, not '256K').\\n\"\n-                                        f\"  Falling back to auto-detected context window.\\n\",\n-                                        file=sys.stderr,\n-                                    )\n-                    break\n+                _cp_ctx_resolved = None\n+\n+            # Surface a clear warning if the user set a context_length but it\n+            # wasn't a valid positive int — the helper silently skips those.\n+            if _config_context_length is None:\n+                _target = self.base_url.rstrip(\"/\") if self.base_url else \"\"\n+                for _cp_entry in _custom_providers:\n+                    if not isinstance(_cp_entry, dict):\n+                        continue\n+                    _cp_url = (_cp_entry.get(\"base_url\") or \"\").rstrip(\"/\")\n+                    if _target and _cp_url == _target:\n+                        _cp_models = _cp_entry.get(\"models\", {})\n+                        if isinstance(_cp_models, dict):\n+                            _cp_model_cfg = _cp_models.get(self.model, {})\n+                            if isinstance(_cp_model_cfg, dict):\n+                                _cp_ctx = _cp_model_cfg.get(\"context_length\")\n+                                if _cp_ctx is not None:\n+                                    try:\n+                                        _parsed = int(_cp_ctx)\n+                                        if _parsed <= 0:\n+                                            raise ValueError\n+                                    except (TypeError, ValueError):\n+                                        logger.warning(\n+                                            \"Invalid context_length for model %r in \"\n+                                            \"custom_providers: %r — must be a positive \"\n+                                            \"integer (e.g. 256000, not '256K'). \"\n+                                            \"Falling back to auto-detection.\",\n+                                            self.model, _cp_ctx,\n+                                        )\n+                                        print(\n+                                            f\"\\n⚠ Invalid context_length for model {self.model!r} in custom_providers: {_cp_ctx!r}\\n\"\n+                                            f\"  Must be a positive integer (e.g. 256000, not '256K').\\n\"\n+                                            f\"  Falling back to auto-detected context window.\\n\",\n+                                            file=sys.stderr,\n+                                        )\n+                        break\n         \n         # Select context engine: config-driven (like memory providers).\n         # 1. Check config.yaml context.engine setting","@@ -1851,6 +1872,7 @@ def __init__(\n                 api_key=[redacted:secret-assignment] \"api_key\", \"\"),\n                 config_context_length=_config_context_length,\n                 provider=self.provider,\n+                custom_providers=_custom_providers,\n             )\n             self.context_compressor.update_model(\n                 model=self.model,","@@ -2141,12 +2163,23 @@ def switch_model(self, new_model, new_provider, api_key='', base_url='', api_mod\n         # ── Update context compressor ──\n         if hasattr(self, \"context_compressor\") and self.context_compressor:\n             from agent.model_metadata import get_model_context_length\n+            # Re-read custom_providers from live config so per-model\n+            # context_length overrides are honored when switching to a\n+            # custom provider mid-session (closes #15779).\n+            _sm_custom_providers = None\n+            try:\n+                from hermes_cli.config import load_config, get_compatible_custom_providers\n+                _sm_cfg = load_config()\n+                _sm_custom_providers = get_compatible_custom_providers(_sm_cfg)\n+            except Exception:\n+                _sm_custom_providers = None\n             new_context_length = get_model_context_length(\n                 self.model,\n                 base_url=self.base_url,\n                 api_key=[redacted:secret-assignment]\n                 provider=self.provider,\n                 config_context_length=getattr(self, \"_config_context_length\", None),\n+                custom_providers=_sm_custom_providers,\n             )\n             self.context_compressor.update_model(\n                 model=self.model,"]},{"file":"tests/agent/test_model_metadata.py","hunks":["@@ -459,9 +459,10 @@ def test_qwen_generic_context_length(self, mock_fetch):\n \n     @patch(\"agent.model_metadata.fetch_model_metadata\")\n     def test_api_missing_context_length_key(self, mock_fetch):\n-        \"\"\"Model in API but without context_length → defaults to 128000.\"\"\"\n+        \"\"\"Model in API but without context_length → defaults to the top\n+        probe tier (currently 256K).\"\"\"\n         mock_fetch.return_value = {\"test/model\": {\"name\": \"Test\"}}\n-        assert get_model_context_length(\"test/model\") == 128000\n+        assert get_model_context_length(\"test/model\") == CONTEXT_PROBE_TIERS[0]\n \n     @patch(\"agent.model_metadata.fetch_model_metadata\")\n     def test_cache_takes_priority_over_api(self, mock_fetch, tmp_path):","@@ -814,14 +815,17 @@ def test_tiers_descending(self):\n         for i in range(len(CONTEXT_PROBE_TIERS) - 1):\n             assert CONTEXT_PROBE_TIERS[i] > CONTEXT_PROBE_TIERS[i + 1]\n \n-    def test_first_tier_is_128k(self):\n-        assert CONTEXT_PROBE_TIERS[0] == 128_000\n+    def test_first_tier_is_256k(self):\n+        assert CONTEXT_PROBE_TIERS[0] == 256_000\n \n     def test_last_tier_is_8k(self):\n         assert CONTEXT_PROBE_TIERS[-1] == 8_000\n \n \n class TestGetNextProbeTier:\n+    def test_from_256k(self):\n+        assert get_next_probe_tier(256_000) == 128_000\n+\n     def test_from_128k(self):\n         assert get_next_probe_tier(128_000) == 64_000\n ","@@ -841,8 +845,8 @@ def test_from_arbitrary_value(self):\n         assert get_next_probe_tier(100_000) == 64_000\n \n     def test_above_max_tier(self):\n-        \"\"\"Value above 128K should return 128K.\"\"\"\n-        assert get_next_probe_tier(500_000) == 128_000\n+        \"\"\"Value above 256K should return 256K.\"\"\"\n+        assert get_next_probe_tier(500_000) == 256_000\n \n     def test_zero_returns_none(self):\n         assert get_next_probe_tier(0) is None"]},{"file":"tests/gateway/test_session_info.py","hunks":["@@ -58,7 +58,7 @@ def test_default_fallback_hint(self, runner, tmp_path):\n                                   {\"provider\": \"\", \"base_url\": \"\", \"api_key\": \"\"})\n         with p1, p2, p3:\n             info = runner._format_session_info()\n-        assert \"128K\" in info\n+        assert \"256K\" in info\n         assert \"model.context_length\" in info\n \n     def test_local_endpoint_shown(self, runner, tmp_path):"]},{"file":"tests/hermes_cli/test_custom_provider_context_length.py","hunks":["@@ -0,0 +1,240 @@\n+\"\"\"Regression tests for custom_providers per-model context_length resolution.\n+\n+Covers the fix for #15779 — mid-session /model switch to a named custom\n+provider must honor ``custom_providers[].models.<id>.context_length`` the\n+same way startup already does.\n+\"\"\"\n+from __future__ import annotations\n+\n+from unittest.mock import patch\n+\n+from hermes_cli.config import get_custom_provider_context_length\n+\n+\n+class TestGetCustomProviderContextLength:\n+    def test_returns_override_for_matching_entry(self):\n+        custom = [\n+            {\n+                \"name\": \"my-endpoint\",\n+                \"base_url\": \"https://example.invalid/v1\",\n+                \"models\": {\"gpt-5.5\": {\"context_length\": 1_050_000}},\n+            }\n+        ]\n+        assert (\n+            get_custom_provider_context_length(\n+                \"gpt-5.5\", \"https://example.invalid/v1\", custom\n+            )\n+            == 1_050_000\n+        )\n+\n+    def test_trailing_slash_insensitive(self):\n+        custom = [\n+            {\n+                \"base_url\": \"https://example.invalid/v1/\",\n+                \"models\": {\"m\": {\"context_length\": 500_000}},\n+            }\n+        ]\n+        # config has trailing slash, runtime doesn't — must match\n+        assert (\n+            get_custom_provider_context_length(\n+                \"m\", \"https://example.invalid/v1\", custom\n+            )\n+            == 500_000\n+        )\n+        # and the reverse\n+        custom2 = [\n+            {\n+                \"base_url\": \"https://example.invalid/v1\",\n+                \"models\": {\"m\": {\"context_length\": 500_000}},\n+            }\n+        ]\n+        assert (\n+            get_custom_provider_context_length(\n+                \"m\", \"https://example.invalid/v1/\", custom2\n+            )\n+            == 500_000\n+        )\n+\n+    def test_returns_none_when_url_does_not_match(self):\n+        custom = [\n+            {\n+                \"base_url\": \"https://example.invalid/v1\",\n+                \"models\": {\"m\": {\"context_length\": 400_000}},\n+            }\n+        ]\n+        assert (\n+            get_custom_provider_context_length(\n+                \"m\", \"https://other.invalid/v1\", custom\n+            )\n+            is None\n+        )\n+\n+    def test_returns_none_when_model_does_not_match(self):\n+        custom = [\n+            {\n+                \"base_url\": \"https://example.invalid/v1\",\n+                \"models\": {\"gpt-5.5\": {\"context_length\": 400_000}},\n+            }\n+        ]\n+        assert (\n+            get_custom_provider_context_length(\n+                \"different-model\", \"https://example.invalid/v1\", custom\n+            )\n+            is None\n+        )\n+\n+    def test_returns_none_for_string_value(self):\n+        \"\"\"'256K' string is not a valid int — skip silently.\n+\n+        (The inline startup path still emits a user-visible warning; the\n+        helper itself returns None so downstream fallbacks can run.)\n+        \"\"\"\n+        custom = [\n+            {\n+                \"base_url\": \"https://example.invalid/v1\",\n+                \"models\": {\"m\": {\"context_length\": \"256K\"}},\n+            }\n+        ]\n+        assert (\n+            get_custom_provider_context_length(\n+                \"m\", \"https://example.invalid/v1\", custom\n+            )\n+            is None\n+        )\n+\n+    def test_returns_none_for_zero_or_negative(self):\n+        for bad in (0, -1, -100):\n+            custom = [\n+                {\n+                    \"base_url\": \"https://example.invalid/v1\",\n+                    \"models\": {\"m\": {\"context_length\": bad}},\n+                }\n+            ]\n+            assert (\n+                get_custom_provider_context_length(\n+                    \"m\", \"https://example.invalid/v1\", custom\n+                )\n+                is None\n+            ), f\"value {bad!r} should be rejected\"\n+\n+    def test_empty_inputs_return_none(self):\n+        assert get_custom_provider_context_length(\"\", \"http://x\", [{\"base_url\": \"http://x\", \"models\": {\"\": {\"context_length\": 1}}}]) is None\n+        assert get_custom_provider_context_length(\"m\", \"\", [{\"base_url\": \"\", \"models\": {\"m\": {\"context_length\": 1}}}]) is None\n+        assert get_custom_provider_context_length(\"m\", \"http://x\", None) is None\n+        assert get_custom_provider_context_length(\"m\", \"http://x\", []) is None\n+\n+    def test_ignores_non_dict_entries(self):\n+        \"\"\"Malformed entries must not crash the lookup.\"\"\"\n+        custom = [\n+            \"not a dict\",\n+            None,\n+            {\"base_url\": \"https://example.invalid/v1\", \"models\": \"not a dict\"},\n+            {\"base_url\": \"https://example.invalid/v1\", \"models\": {\"m\": \"not a dict\"}},\n+            {\n+                \"base_url\": \"https://example.invalid/v1\",\n+                \"models\": {\"m\": {\"context_length\": 400_000}},\n+            },\n+        ]\n+        assert (\n+            get_custom_provider_context_length(\n+                \"m\", \"https://example.invalid/v1\", custom\n+            )\n+            == 400_000\n+        )\n+\n+\n+class TestGetModelContextLengthHonorsOverride:\n+    \"\"\"agent.model_metadata.get_model_context_length must honor the\n+    custom_providers override at step 0b — before any probe, cache hit,\n+    or models.dev lookup can override it.\n+    \"\"\"\n+\n+    def _mock_all_probes(self):\n+        \"\"\"Context manager that disables every downstream resolution step.\"\"\"\n+        from agent import model_metadata as _mm\n+        return [\n+            patch.object(_mm, \"get_cached_context_length\", return_value=None),\n+            patch.object(_mm, \"fetch_endpoint_model_metadata\", return_value={}),\n+            patch.object(_mm, \"fetch_model_metadata\", return_value={}),\n+            patch.object(_mm, \"is_local_endpoint\", return_value=False),\n+            patch.object(_mm, \"_is_known_provider_base_url\", return_value=False),\n+        ]\n+\n+    def test_custom_providers_override_wins_over_default_fallback(self):\n+        from agent.model_metadata import get_model_context_length\n+        custom = [\n+            {\n+                \"base_url\": \"https://example.invalid/v1\",\n+                \"models\": {\"gpt-5.5\": {\"context_length\": 1_050_000}},\n+            }\n+        ]\n+        patches = self._mock_all_probes()\n+        for p in patches:\n+            p.start()\n+        try:\n+            ctx = get_model_context_length(\n+                \"gpt-5.5\",\n+                base_url=\"https://example.invalid/v1\",\n+                provider=\"custom\",\n+                custom_providers=custom,\n+            )\n+        finally:\n+            for p in patches:\n+                p.stop()\n+        assert ctx == 1_050_000\n+\n+    def test_explicit_config_context_length_still_wins(self):\n+        \"\"\"Top-level model.context_length (step 0) outranks custom_providers (step 0b).\n+\n+        Users who set both should see the top-level value — that's the\n+        documented precedence and matches the long-standing step-0 behavior.\n+        \"\"\"\n+        from agent.model_metadata import get_model_context_length\n+        custom = [\n+            {\n+                \"base_url\": \"https://example.invalid/v1\",\n+                \"models\": {\"m\": {\"context_length\": 1_050_000}},\n+            }\n+        ]\n+        ctx = get_model_context_length(\n+            \"m\",\n+            base_url=\"https://example.invalid/v1\",\n+            provider=\"custom\",\n+            config_context_length=500_000,  # explicit top-level wins\n+            custom_providers=custom,\n+        )\n+        assert ctx == 500_000\n+\n+    def test_no_override_falls_through_to_default(self):\n+        \"\"\"With custom_providers=None and all probes disabled, resolver\n+        returns DEFAULT_FALLBACK_CONTEXT (256K after the stepdown bump).\n+        \"\"\"\n+        from agent.model_metadata import get_model_context_length, DEFAULT_FALLBACK_CONTEXT\n+        patches = self._mock_all_probes()\n+        for p in patches:\n+            p.start()\n+        try:\n+            ctx = get_model_context_length(\n+                \"unknown-model\",\n+                base_url=\"https://example.invalid/v1\",\n+                provider=\"custom\",\n+                custom_providers=None,\n+            )\n+        finally:\n+            for p in patches:\n+                p.stop()\n+        assert ctx == DEFAULT_FALLBACK_CONTEXT\n+\n+\n+class TestContextProbeTiers:\n+    def test_256k_is_top_tier_and_default(self):\n+        \"\"\"The stepdown probe starts at 256K and 256K is the new default.\"\"\"\n+        from agent.model_metadata import CONTEXT_PROBE_TIERS, DEFAULT_FALLBACK_CONTEXT\n+\n+        assert CONTEXT_PROBE_TIERS[0] == 256_000\n+        assert DEFAULT_FALLBACK_CONTEXT == 256_000\n+        # Tiers still descend monotonically\n+        for a, b in zip(CONTEXT_PROBE_TIERS, CONTEXT_PROBE_TIERS[1:]):\n+            assert a > b, f\"tiers must strictly descend, got {a} then {b}\"\n+        # 128K is still a tier (users relying on it probe-down get there)\n+        assert 128_000 in CONTEXT_PROBE_TIERS"]},{"file":"tests/hermes_cli/test_model_switch_context_display.py","hunks":["@@ -88,3 +88,61 @@ def test_prefers_resolver_even_when_model_info_has_larger_value(self):\n                 model_info=fake_mi,\n             )\n         assert ctx == 128_000\n+\n+    def test_custom_providers_override_honored(self):\n+        \"\"\"Regression for #15779: /model switch onto a custom provider must\n+        surface the configured per-model context_length, not the 128K/256K\n+        fallback.\n+        \"\"\"\n+        custom_provs = [\n+            {\n+                \"name\": \"my-custom-endpoint\",\n+                \"base_url\": \"https://example.invalid/v1\",\n+                \"models\": {\"gpt-5.5\": {\"context_length\": 1_050_000}},\n+            }\n+        ]\n+        # Real resolver call — no mock — so the override path is exercised\n+        # through agent.model_metadata.get_model_context_length.\n+        from unittest.mock import patch as _p\n+        from agent import model_metadata as _mm\n+        with _p.object(_mm, \"get_cached_context_length\", return_value=None), \\\n+             _p.object(_mm, \"fetch_endpoint_model_metadata\", return_value={}), \\\n+             _p.object(_mm, \"fetch_model_metadata\", return_value={}), \\\n+             _p.object(_mm, \"is_local_endpoint\", return_value=False), \\\n+             _p.object(_mm, \"_is_known_provider_base_url\", return_value=False):\n+            ctx = resolve_display_context_length(\n+                \"gpt-5.5\",\n+                \"custom\",\n+                base_url=\"https://example.invalid/v1\",\n+                api_key=\"k\",\n+                custom_providers=custom_provs,\n+            )\n+        assert ctx == 1_050_000, (\n+            \"custom_providers[].models.gpt-5.5.context_length=1.05M must win \"\n+            \"over probe-down fallback\"\n+        )\n+\n+    def test_custom_providers_trailing_slash_insensitive(self):\n+        \"\"\"Base URL comparison must tolerate trailing-slash differences\n+        between config.yaml and the runtime value.\n+        \"\"\"\n+        custom_provs = [\n+            {\n+                \"base_url\": \"https://example.invalid/v1/\",\n+                \"models\": {\"m\": {\"context_length\": 400_000}},\n+            }\n+        ]\n+        from unittest.mock import patch as _p\n+        from agent import model_metadata as _mm\n+        with _p.object(_mm, \"get_cached_context_length\", return_value=None), \\\n+             _p.object(_mm, \"fetch_endpoint_model_metadata\", return_value={}), \\\n+             _p.object(_mm, \"fetch_model_metadata\", return_value={}), \\\n+             _p.object(_mm, \"is_local_endpoint\", return_value=False), \\\n+             _p.object(_mm, \"_is_known_provider_base_url\", return_value=False):\n+            ctx = resolve_display_context_length(\n+                \"m\",\n+                \"custom\",\n+                base_url=\"https://example.invalid/v1\",  # no trailing slash\n+                custom_providers=custom_provs,\n+            )\n+        assert ctx == 400_000\n"]}]},{"kind":"pr","number":15948,"title":"Fix config-driven context length resolution","url":null,"state":"open","close_reason":{"refs":[{"kind":"pr","value":"#15907","number":15907},{"kind":"pr","value":"#15907","number":15907},{"kind":"pr","value":"#15907","number":15907}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":16918,"title":"fix(run_agent): use aux provider for compression context length lookup","url":null,"state":"closed","close_reason":{"category":"silent_close","implication":"active"},"merged":false},{"kind":"pr","number":17460,"title":"fix(model_metadata): auto-load global custom_providers in get_model_context_length (closes #12977) (closes #13807)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"7becb19ea00c13bdff6f78b71aa3ddfb0bdb5378","number":0}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":18844,"title":"refactor(gateway): use get_custom_provider_context_length() helper + pass custom_providers to @context path","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"8206e4249749","number":0},{"kind":"commit","value":"265ac7d81219","number":0},{"kind":"commit","value":"781334eea4","number":0},{"kind":"pr","value":"#133822","number":133822}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":18877,"title":"fix(context): pass custom_providers in aux compression and fallback paths","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"7becb19ea00c13bdff6f78b71aa3ddfb0bdb5378","number":0},{"kind":"commit","value":"21078ebcea6dd870835080fdc76a40284418c921","number":0},{"kind":"commit","value":"b5bcffe1674fa9ab3ba7a754c07ab77bedde83a8","number":0},{"kind":"commit","value":"563b4d9e51a46cc421e327b351cb7efe1ccb151b","number":0}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":19034,"title":"fix(cli): honor custom context lengths in model switch display","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#15844","number":15844}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":19553,"title":"fix(compression): pass custom_providers to feasibility check context lookup","url":null,"state":"closed","close_reason":{"category":"stale_abandoned","implication":"active"},"merged":false},{"kind":"pr","number":20218,"title":"fix(run_agent): use aux provider for compression context length lookup (salvage #16918)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["run_agent.py","scripts/release.py"]},{"kind":"pr","number":20724,"title":"fix(run_agent): pass custom_providers to compression feasibility check (#20608)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"7becb19ea00c13bdff6f78b71aa3ddfb0bdb5378","number":0},{"kind":"commit","value":"7becb19ea00c13bdff6f78b71aa3ddfb0bdb5378","number":0},{"kind":"pr","value":"#20608","number":20608}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":21953,"title":"fix(compression): honor custom provider context lengths","url":null,"state":"closed","close_reason":{"category":"stale_abandoned","implication":"active"},"merged":false},{"kind":"pr","number":23563,"title":"fix(agent): pass custom providers to aux context checks","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#13540","number":13540},{"kind":"pr","value":"#18877","number":18877},{"kind":"pr","value":"#20724","number":20724}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":23788,"title":"fix(compression): pass custom providers to feasibility check","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"7becb19ea00c13bdff6f78b71aa3ddfb0bdb5378","number":0},{"kind":"commit","value":"7becb19ea","number":0}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":23789,"title":"fix: pass custom_providers to get_model_context_length in compression feasibility check","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"7becb19ea00c13bdff6f78b71aa3ddfb0bdb5378","number":0}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":24079,"title":"fix: clear stale _config_context_length when switching providers","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"b6d48ebfde63f325143bc601cbc622d89d428f1a","number":0},{"kind":"pr","value":"#24724","number":24724},{"kind":"pr","value":"#22387","number":22387}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":25494,"title":"fix(auxiliary): forward custom_providers to compression model context-length detection (#25359)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["run_agent.py"],"fix_hunks":[{"file":"run_agent.py","hunks":["@@ -2204,6 +2204,10 @@ def __init__(\n             if not isinstance(_custom_providers, list):\n                 _custom_providers = []\n \n+        # Store for reuse by _check_compression_model_feasibility (auxiliary\n+        # compression model context-length detection needs the same list).\n+        self._custom_providers = _custom_providers\n+\n         # Check custom_providers per-model context_length\n         if _config_context_length is None and _custom_providers:\n             try:","@@ -3246,6 +3250,7 @@ def _check_compression_model_feasibility(self) -> None:\n                 # provider-specific paths (e.g. Bedrock static table, OpenRouter API)\n                 # are invoked for the correct client, not inherited from the main model.\n                 provider=(_aux_cfg_provider if _aux_cfg_provider and _aux_cfg_provider != \"auto\" else getattr(self, \"provider\", \"\")),\n+                custom_providers=self._custom_providers,\n             )\n \n             # Hard floor: the auxiliary compression model must have at least\n"]}]},{"kind":"pr","number":25554,"title":"fix(agent): forward custom_providers to context-length probe on fallback activation","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"b5bcffe1674fa9ab3ba7a754c07ab77bedde83a8","number":0}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":26843,"title":"fix(compression): pass custom providers to context resolver","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"125de02056eab84362fc91f57bd7041a19860b22","number":0},{"kind":"pr","value":"#15844","number":15844}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":36852,"title":"fix(custom_providers): resolve per-model context_length for publisher/slug model ids","url":null,"state":"open","close_reason":{"refs":[{"kind":"commit","value":"265ac7d812192d3b6f11888811512604681d3a32","number":0}],"category":"uncategorized","implication":"needs_llm"},"merged":false},{"kind":"pr","number":52188,"title":"fix(lmstudio): respect applied runtime context","url":null,"state":"closed","close_reason":{"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":59522,"title":"fix(cli): pass custom_providers to resolve_display_context_length (#59314)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["cli.py","tests/hermes_cli/test_model_switch_context_display.py"]},{"kind":"pr","number":73578,"title":"fix(lmstudio): respect applied runtime context (salvage of #52188)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/agent_init.py","agent/agent_runtime_helpers.py","contributors/emails/[redacted:email]","hermes_cli/models.py","run_agent.py","tests/hermes_cli/test_lmstudio_context_policy.py","tests/hermes_cli/test_urllib_security.py","tests/run_agent/test_lmstudio_load_mode.py","tests/run_agent/test_switch_model_context.py","website/docs/integrations/providers.md"]},{"kind":"pr","number":133822,"title":"Mem0 installs from Mem0's own catalog plugin; core no longer bundles it","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["CONTRIBUTING.es.md","CONTRIBUTING.md","hermes_cli/config_defaults.py","hermes_cli/doctor_state.py","hermes_cli/subcommands/memory.py","locales/af.yaml","locales/ar.yaml","locales/de.yaml","locales/en.yaml","locales/es.yaml","locales/fr.yaml","locales/ga.yaml","locales/hu.yaml","locales/it.yaml","locales/ja.yaml","locales/ko.yaml","locales/pt.yaml","locales/ru.yaml","locales/tr.yaml","locales/uk.yaml","locales/zh-hant.yaml","locales/zh.yaml","plugins/AGENTS.md","plugins/memory/mem0/README.md","plugins/memory/mem0/__init__.py","plugins/memory/mem0/_backend.py","plugins/memory/mem0/_openai_llm.py","plugins/memory/mem0/_oss_providers.py","plugins/memory/mem0/_setup.py","plugins/memory/mem0/plugin.yaml","pm/extras.py","pyproject.toml","scripts/iso-certify.py","tests/agent/test_memory_user_id.py","tests/e2e/core/upgrade/test_fresh_process_entrypoints.py","tests/hermes_cli/test_doctor.py","tests/plugins/memory/test_mem0_backend.py","tests/plugins/memory/test_mem0_backend_integration.py","tests/plugins/memory/test_mem0_providers.py","tests/plugins/memory/test_mem0_setup.py","tests/plugins/memory/test_mem0_v3.py","tests/plugins/memory/test_memory_lazy_install.py","tests/plugins/memory/test_multiplex_memory_identity_scope.py","tests/plugins/memory/test_provider_threads_inherit_profile.py","tests/plugins/test_plugin_paths_follow_profile.py","tests/test_project_metadata.py","website/docs/getting-started/nix-setup.md","website/docs/integrations/index.md","website/docs/reference/cli-commands.md","website/docs/user-guide/features/memory-providers.md","website/docs/user-guide/features/memory.md","website/docs/user-guide/features/overview.md","website/docs/user-guide/windows-native.md"]}],"trust":{"default_tier":"verified","includes_suggested":false}}