diff --git a/agent/auxiliary_client.py b/agent/auxiliary_client.py index 89dc7d935b47d..cabf54a9ea7ad 100644 --- a/agent/auxiliary_client.py +++ b/agent/auxiliary_client.py @@ -2933,9 +2933,15 @@ def _resolve_auto(main_runtime: Optional[Dict[str, Any]] = None) -> Tuple[Option explicit_base_url = None explicit_api_key = None if runtime_base_url and (main_provider == "custom" or main_provider.startswith("custom:")): - resolved_provider = "custom" - explicit_base_url = runtime_base_url - explicit_api_key = runtime_api_key or None + # For named custom providers (custom:name), don't rewrite to bare "custom" — + # let it fall through to the named custom provider branch in + # resolve_provider_client which has proper api_mode handling. + # Only rewrite bare "custom" (anonymous custom endpoint). + if main_provider == "custom": + resolved_provider = "custom" + explicit_base_url = runtime_base_url + explicit_api_key = runtime_api_key or None + # else: keep main_provider as-is, explicit_base_url=None → named custom branch # Skip Step-1 if the main provider was recently 402'd. The unhealthy # cache TTL bounds how long we bypass it, so a topped-up account # recovers automatically. If we tried Step-1 anyway, every aux call @@ -3378,7 +3384,7 @@ def _wrap_if_needed(client_obj, final_model_str: str, base_url_str: str = "", # /v1 equivalent. Rewrite only on the OpenAI-wire path so the # Anthropic fallback SDK still sees the original URL. if entry_api_mode == "anthropic_messages": - openai_base = custom_base + openai_base = _to_openai_base_url(custom_base) raw_base_for_wrap = custom_base else: openai_base = _to_openai_base_url(custom_base) diff --git a/tools/browser_tool.py b/tools/browser_tool.py index 447f65007140f..55d89eb0d8df5 100644 --- a/tools/browser_tool.py +++ b/tools/browser_tool.py @@ -197,6 +197,22 @@ def _merge_browser_path(existing_path: str = "") -> str: _command_timeout_resolved = False +def _get_stealth_args() -> tuple[list[str], str]: + """Read stealth config from config.yaml and return (args_list, user_agent). + + Returns ([], "") when stealth is not configured or unreadable. + """ + try: + from hermes_cli.config import read_raw_config + config = read_raw_config() + stealth = config.get("browser", {}).get("stealth", {}) + args = stealth.get("args", []) + user_agent = stealth.get("user_agent", "") or "" + return list(args), user_agent + except Exception: + return [], "" + + def _get_command_timeout() -> int: """Return the configured browser command timeout from config.yaml. @@ -1970,6 +1986,13 @@ def _run_browser_command( else: cmd_prefix = [browser_cmd] + # Inject stealth args + user-agent from config + stealth_args, stealth_ua = _get_stealth_args() + if stealth_args: + args = args + ["--args", ",".join(stealth_args)] + if stealth_ua: + args = args + ["--user-agent", stealth_ua] + cmd_parts = cmd_prefix + backend_args + [ "--json", command diff --git a/tools/send_message_tool.py b/tools/send_message_tool.py index 1fb8365a02705..8d67a96cebff3 100644 --- a/tools/send_message_tool.py +++ b/tools/send_message_tool.py @@ -749,7 +749,8 @@ async def _send_to_platform(platform, pconfig, chat_id, message, thread_id=None, ) last_result = None - for chunk in chunks: + for i, chunk in enumerate(chunks): + is_last = (i == len(chunks) - 1) if platform == Platform.SLACK: result = await _send_slack(pconfig.token, chat_id, chunk) elif platform == Platform.WHATSAPP: @@ -769,7 +770,11 @@ async def _send_to_platform(platform, pconfig, chat_id, message, thread_id=None, elif platform == Platform.DINGTALK: result = await _send_dingtalk(pconfig.extra, chat_id, chunk) elif platform == Platform.FEISHU: - result = await _send_feishu(pconfig, chat_id, chunk, thread_id=thread_id) + result = await _send_feishu( + pconfig, chat_id, chunk, + media_files=media_files if is_last else [], + thread_id=thread_id, + ) elif platform == Platform.WECOM: result = await _send_wecom(pconfig.extra, chat_id, chunk) elif platform == Platform.BLUEBUBBLES: