Merge branch 'main' of github.com:NousResearch/hermes-agent into bb/gui
This commit is contained in:
@@ -14,8 +14,8 @@ Provides subcommands for:
|
||||
import os
|
||||
import sys
|
||||
|
||||
__version__ = "0.13.0"
|
||||
__release_date__ = "2026.5.7"
|
||||
__version__ = "0.14.0"
|
||||
__release_date__ = "2026.5.16"
|
||||
|
||||
|
||||
def _ensure_utf8():
|
||||
|
||||
@@ -1152,6 +1152,10 @@ DEFAULT_CONFIG = {
|
||||
"provider": "", # e.g. "openrouter" (empty = inherit parent provider + credentials)
|
||||
"base_url": "", # direct OpenAI-compatible endpoint for subagents
|
||||
"api_key": "", # API key for delegation.base_url (falls back to OPENAI_API_KEY)
|
||||
"api_mode": "", # wire protocol for delegation.base_url: "chat_completions",
|
||||
# "codex_responses", or "anthropic_messages". Empty = auto-detect
|
||||
# from URL (e.g. /anthropic suffix → anthropic_messages). Set this
|
||||
# explicitly for non-standard endpoints the heuristic can't detect.
|
||||
# When delegate_task narrows child toolsets explicitly, preserve any
|
||||
# MCP toolsets the parent already has enabled. On by default so
|
||||
# narrowing (e.g. toolsets=["web","browser"]) expresses "I want these
|
||||
@@ -1609,6 +1613,23 @@ DEFAULT_CONFIG = {
|
||||
"servers": {},
|
||||
},
|
||||
|
||||
# X (Twitter) Search via xAI's built-in x_search Responses tool.
|
||||
# The tool registers when xAI credentials are available (SuperGrok
|
||||
# OAuth or XAI_API_KEY) AND the x_search toolset is enabled in
|
||||
# `hermes tools`. These settings tune the backing Responses API call.
|
||||
"x_search": {
|
||||
# xAI model used for the Responses call. grok-4.20-reasoning is
|
||||
# the recommended default; any Grok model with x_search tool
|
||||
# access works.
|
||||
"model": "grok-4.20-reasoning",
|
||||
# Request timeout in seconds (minimum 30). x_search can take
|
||||
# 60-120s for complex queries — the default is generous.
|
||||
"timeout_seconds": 180,
|
||||
# Number of automatic retries on 5xx / ReadTimeout / ConnectionError.
|
||||
# Each retry backs off (1.5x attempt seconds, capped at 5s).
|
||||
"retries": 2,
|
||||
},
|
||||
|
||||
# Config schema version - bump this when adding new required fields
|
||||
"_config_version": 23,
|
||||
}
|
||||
|
||||
+28
-1
@@ -152,6 +152,30 @@ def _apply_doctor_tool_availability_overrides(available: list[str], unavailable:
|
||||
return updated_available, updated_unavailable
|
||||
|
||||
|
||||
def _has_healthy_oauth_fallback_for_apikey_provider(provider_label: str) -> bool:
|
||||
"""Return True when a direct API-key probe failure is non-blocking.
|
||||
|
||||
Some provider families support both a direct API-key path and a separate
|
||||
OAuth runtime path. When the OAuth path is already healthy, doctor should
|
||||
still show a failed API-key connectivity row, but it should not promote
|
||||
that direct-key problem into the final blocking summary.
|
||||
"""
|
||||
try:
|
||||
from hermes_cli.auth import (
|
||||
get_gemini_oauth_auth_status,
|
||||
get_minimax_oauth_auth_status,
|
||||
)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
normalized = (provider_label or "").strip().lower()
|
||||
if normalized in {"google / gemini", "gemini"}:
|
||||
return bool((get_gemini_oauth_auth_status() or {}).get("logged_in"))
|
||||
if normalized == "minimax":
|
||||
return bool((get_minimax_oauth_auth_status() or {}).get("logged_in"))
|
||||
return False
|
||||
|
||||
|
||||
def check_ok(text: str, detail: str = ""):
|
||||
print(f" {color('✓', Colors.GREEN)} {text}" + (f" {color(detail, Colors.DIM)}" if detail else ""))
|
||||
|
||||
@@ -1594,7 +1618,10 @@ def run_doctor(args):
|
||||
print(f" {_glyph} {_label} {_detail}")
|
||||
else:
|
||||
print(f" {_glyph} {_label}")
|
||||
for _issue in _r.issues:
|
||||
_issues_to_add = list(_r.issues)
|
||||
if _issues_to_add and _has_healthy_oauth_fallback_for_apikey_provider(_r.label):
|
||||
_issues_to_add = []
|
||||
for _issue in _issues_to_add:
|
||||
issues.append(_issue)
|
||||
|
||||
# =========================================================================
|
||||
|
||||
@@ -2525,6 +2525,7 @@ def _is_github_models_base_url(base_url: Optional[str]) -> bool:
|
||||
return (
|
||||
normalized.startswith(COPILOT_BASE_URL)
|
||||
or normalized.startswith("https://models.github.ai/inference")
|
||||
or normalized.startswith("https://models.inference.ai.azure.com")
|
||||
)
|
||||
|
||||
|
||||
|
||||
+13
-2
@@ -325,8 +325,15 @@ class PluginContext:
|
||||
is_async: bool = False,
|
||||
description: str = "",
|
||||
emoji: str = "",
|
||||
override: bool = False,
|
||||
) -> None:
|
||||
"""Register a tool in the global registry **and** track it as plugin-provided."""
|
||||
"""Register a tool in the global registry **and** track it as plugin-provided.
|
||||
|
||||
Pass ``override=True`` to replace an existing built-in tool with the
|
||||
same name (e.g. swap the default ``browser_navigate`` for a custom
|
||||
CDP-backed implementation). Without it, attempting to register a name
|
||||
already claimed by a different toolset is rejected.
|
||||
"""
|
||||
from tools.registry import registry
|
||||
|
||||
registry.register(
|
||||
@@ -339,9 +346,13 @@ class PluginContext:
|
||||
is_async=is_async,
|
||||
description=description,
|
||||
emoji=emoji,
|
||||
override=override,
|
||||
)
|
||||
self._manager._plugin_tool_names.add(name)
|
||||
logger.debug("Plugin %s registered tool: %s", self.manifest.name, name)
|
||||
logger.debug(
|
||||
"Plugin %s registered tool: %s%s",
|
||||
self.manifest.name, name, " (override)" if override else "",
|
||||
)
|
||||
|
||||
# -- message injection --------------------------------------------------
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ CONFIGURABLE_TOOLSETS = [
|
||||
("video", "🎬 Video Analysis", "video_analyze (requires video-capable model)"),
|
||||
("image_gen", "🎨 Image Generation", "image_generate"),
|
||||
("video_gen", "🎬 Video Generation", "video_generate (text-to-video + image-to-video)"),
|
||||
("x_search", "🐦 X (Twitter) Search", "x_search (requires xAI OAuth or XAI_API_KEY)"),
|
||||
("moa", "🧠 Mixture of Agents", "mixture_of_agents"),
|
||||
("tts", "🔊 Text-to-Speech", "text_to_speech"),
|
||||
("skills", "📚 Skills", "list, view, manage"),
|
||||
@@ -86,7 +87,12 @@ CONFIGURABLE_TOOLSETS = [
|
||||
# Video gen is off by default — it's a niche, paid, slow feature. Users
|
||||
# who want it opt in via `hermes tools` → Video Generation, which walks
|
||||
# them through provider + model selection.
|
||||
_DEFAULT_OFF_TOOLSETS = {"moa", "homeassistant", "spotify", "discord", "discord_admin", "video", "video_gen"}
|
||||
#
|
||||
# X search is off by default — gated on xAI credentials (SuperGrok OAuth
|
||||
# or XAI_API_KEY). Users opt in via `hermes tools` → X (Twitter) Search,
|
||||
# which walks them through credential setup. The tool's check_fn means
|
||||
# the schema won't appear to the model even if enabled without credentials.
|
||||
_DEFAULT_OFF_TOOLSETS = {"moa", "homeassistant", "spotify", "discord", "discord_admin", "video", "video_gen", "x_search"}
|
||||
|
||||
# Platform-scoped toolsets: only appear in the `hermes tools` checklist for
|
||||
# these platforms, and only resolve/save for these platforms. A toolset
|
||||
@@ -308,6 +314,39 @@ TOOL_CATEGORIES = {
|
||||
# converge image_gen toward.
|
||||
"providers": [],
|
||||
},
|
||||
"x_search": {
|
||||
"name": "X (Twitter) Search",
|
||||
"setup_title": "Select xAI Credential Source",
|
||||
"setup_note": (
|
||||
"Hermes routes X searches through xAI's built-in x_search "
|
||||
"Responses tool. Both credential sources hit the same "
|
||||
"https://api.x.ai/v1/responses endpoint — pick whichever you "
|
||||
"already have. SuperGrok OAuth is preferred when both are set "
|
||||
"(uses your subscription quota instead of API spend)."
|
||||
),
|
||||
"icon": "🐦",
|
||||
"providers": [
|
||||
{
|
||||
"name": "xAI Grok OAuth (SuperGrok Subscription)",
|
||||
"badge": "subscription",
|
||||
"tag": "Browser login at accounts.x.ai — no API key required",
|
||||
"env_vars": [],
|
||||
"post_setup": "xai_grok",
|
||||
},
|
||||
{
|
||||
"name": "xAI API key",
|
||||
"badge": "paid",
|
||||
"tag": "Direct xAI API billing via XAI_API_KEY",
|
||||
"env_vars": [
|
||||
{
|
||||
"key": "XAI_API_KEY",
|
||||
"prompt": "xAI API key",
|
||||
"url": "https://console.x.ai/",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
"browser": {
|
||||
"name": "Browser Automation",
|
||||
"icon": "🌐",
|
||||
|
||||
Reference in New Issue
Block a user