mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
refactor: codebase-wide lint cleanup — unused imports, dead code, and inefficient patterns (#5821)
Comprehensive cleanup across 80 files based on automated (ruff, pyflakes, vulture)
and manual analysis of the entire codebase.
Changes by category:
Unused imports removed (~95 across 55 files):
- Removed genuinely unused imports from all major subsystems
- agent/, hermes_cli/, tools/, gateway/, plugins/, cron/
- Includes imports in try/except blocks that were truly unused
(vs availability checks which were left alone)
Unused variables removed (~25):
- Removed dead variables: connected, inner, channels, last_exc,
source, new_server_names, verify, pconfig, default_terminal,
result, pending_handled, temperature, loop
- Dropped unused argparse subparser assignments in hermes_cli/main.py
(12 instances of add_parser() where result was never used)
Dead code removed:
- run_agent.py: Removed dead ternary (None if False else None) and
surrounding unreachable branch in identity fallback
- run_agent.py: Removed write-only attribute _last_reported_tool
- hermes_cli/providers.py: Removed dead @property decorator on
module-level function (decorator has no effect outside a class)
- gateway/run.py: Removed unused MCP config load before reconnect
- gateway/platforms/slack.py: Removed dead SessionSource construction
Undefined name bugs fixed (would cause NameError at runtime):
- batch_runner.py: Added missing logger = logging.getLogger(__name__)
- tools/environments/daytona.py: Added missing Dict and Path imports
Unnecessary global statements removed (14):
- tools/terminal_tool.py: 5 functions declared global for dicts
they only mutated via .pop()/[key]=value (no rebinding)
- tools/browser_tool.py: cleanup thread loop only reads flag
- tools/rl_training_tool.py: 4 functions only do dict mutations
- tools/mcp_oauth.py: only reads the global
- hermes_time.py: only reads cached values
Inefficient patterns fixed:
- startswith/endswith tuple form: 15 instances of
x.startswith('a') or x.startswith('b') consolidated to
x.startswith(('a', 'b'))
- len(x)==0 / len(x)>0: 13 instances replaced with pythonic
truthiness checks (not x / bool(x))
- in dict.keys(): 5 instances simplified to in dict
- Redefined unused name: removed duplicate _strip_mdv2 import in
send_message_tool.py
Other fixes:
- hermes_cli/doctor.py: Replaced undefined logger.debug() with pass
- hermes_cli/config.py: Consolidated chained .endswith() calls
Test results: 3934 passed, 17 failed (all pre-existing on main),
19 skipped. Zero regressions.
This commit is contained in:
parent
afe6c63c52
commit
d0ffb111c2
80 changed files with 81 additions and 210 deletions
|
|
@ -15,7 +15,6 @@ Usage::
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_constants import get_hermes_home
|
||||||
|
|
|
||||||
|
|
@ -262,8 +262,6 @@ class SessionManager:
|
||||||
if self._db_instance is not None:
|
if self._db_instance is not None:
|
||||||
return self._db_instance
|
return self._db_instance
|
||||||
try:
|
try:
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
from hermes_state import SessionDB
|
from hermes_state import SessionDB
|
||||||
hermes_home = get_hermes_home()
|
hermes_home = get_hermes_home()
|
||||||
self._db_instance = SessionDB(db_path=hermes_home / "state.db")
|
self._db_instance = SessionDB(db_path=hermes_home / "state.db")
|
||||||
|
|
|
||||||
|
|
@ -188,9 +188,7 @@ def _requires_bearer_auth(base_url: str | None) -> bool:
|
||||||
if not base_url:
|
if not base_url:
|
||||||
return False
|
return False
|
||||||
normalized = base_url.rstrip("/").lower()
|
normalized = base_url.rstrip("/").lower()
|
||||||
return normalized.startswith("https://api.minimax.io/anthropic") or normalized.startswith(
|
return normalized.startswith(("https://api.minimax.io/anthropic", "https://api.minimaxi.com/anthropic"))
|
||||||
"https://api.minimaxi.com/anthropic"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def build_anthropic_client(api_key: str, base_url: str = None):
|
def build_anthropic_client(api_key: str, base_url: str = None):
|
||||||
|
|
@ -847,7 +845,7 @@ def _convert_openai_image_part_to_anthropic(part: Dict[str, Any]) -> Optional[Di
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if url.startswith("http://") or url.startswith("https://"):
|
if url.startswith(("http://", "https://")):
|
||||||
return {
|
return {
|
||||||
"type": "image",
|
"type": "image",
|
||||||
"source": {
|
"source": {
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,6 @@ class _CodexCompletionsAdapter:
|
||||||
def create(self, **kwargs) -> Any:
|
def create(self, **kwargs) -> Any:
|
||||||
messages = kwargs.get("messages", [])
|
messages = kwargs.get("messages", [])
|
||||||
model = kwargs.get("model", self._model)
|
model = kwargs.get("model", self._model)
|
||||||
temperature = kwargs.get("temperature")
|
|
||||||
|
|
||||||
# Separate system/instructions from conversation messages.
|
# Separate system/instructions from conversation messages.
|
||||||
# Convert chat.completions multimodal content blocks to Responses
|
# Convert chat.completions multimodal content blocks to Responses
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from agent.memory_provider import MemoryProvider
|
from agent.memory_provider import MemoryProvider
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,21 +10,18 @@ import uuid
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from dataclasses import dataclass, fields, replace
|
from dataclasses import dataclass, fields, replace
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime
|
||||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||||
|
|
||||||
from hermes_constants import OPENROUTER_BASE_URL
|
from hermes_constants import OPENROUTER_BASE_URL
|
||||||
import hermes_cli.auth as auth_mod
|
import hermes_cli.auth as auth_mod
|
||||||
from hermes_cli.auth import (
|
from hermes_cli.auth import (
|
||||||
ACCESS_TOKEN_REFRESH_SKEW_SECONDS,
|
|
||||||
CODEX_ACCESS_TOKEN_REFRESH_SKEW_SECONDS,
|
CODEX_ACCESS_TOKEN_REFRESH_SKEW_SECONDS,
|
||||||
DEFAULT_AGENT_KEY_MIN_TTL_SECONDS,
|
DEFAULT_AGENT_KEY_MIN_TTL_SECONDS,
|
||||||
PROVIDER_REGISTRY,
|
PROVIDER_REGISTRY,
|
||||||
_agent_key_is_usable,
|
|
||||||
_codex_access_token_is_expiring,
|
_codex_access_token_is_expiring,
|
||||||
_decode_jwt_claims,
|
_decode_jwt_claims,
|
||||||
_import_codex_cli_tokens,
|
_import_codex_cli_tokens,
|
||||||
_is_expiring,
|
|
||||||
_load_auth_store,
|
_load_auth_store,
|
||||||
_load_provider_state,
|
_load_provider_state,
|
||||||
_resolve_zai_base_url,
|
_resolve_zai_base_url,
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,9 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
from utils import atomic_json_write
|
from utils import atomic_json_write
|
||||||
|
|
||||||
|
|
@ -231,7 +231,7 @@ def fetch_models_dev(force_refresh: bool = False) -> Dict[str, Any]:
|
||||||
response = requests.get(MODELS_DEV_URL, timeout=15)
|
response = requests.get(MODELS_DEV_URL, timeout=15)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
data = response.json()
|
data = response.json()
|
||||||
if isinstance(data, dict) and len(data) > 0:
|
if isinstance(data, dict) and data:
|
||||||
_models_dev_cache = data
|
_models_dev_cache = data
|
||||||
_models_dev_cache_time = time.time()
|
_models_dev_cache_time = time.time()
|
||||||
_save_disk_cache(data)
|
_save_disk_cache(data)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
from typing import Any, Dict, List, Set, Tuple
|
||||||
|
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_constants import get_hermes_home
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ Inspired by Block/goose's SubdirectoryHintTracker.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import shlex
|
import shlex
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, Optional, Set
|
from typing import Dict, Any, Optional, Set
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ from multiprocessing import Pool, Lock
|
||||||
import traceback
|
import traceback
|
||||||
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn, MofNCompleteColumn
|
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn, MofNCompleteColumn
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
import fire
|
import fire
|
||||||
|
|
||||||
from run_agent import AIAgent
|
from run_agent import AIAgent
|
||||||
|
|
@ -1016,7 +1018,7 @@ class BatchRunner:
|
||||||
tool_stats = data.get('tool_stats', {})
|
tool_stats = data.get('tool_stats', {})
|
||||||
|
|
||||||
# Check for invalid tool names (model hallucinations)
|
# Check for invalid tool names (model hallucinations)
|
||||||
invalid_tools = [k for k in tool_stats.keys() if k not in VALID_TOOLS]
|
invalid_tools = [k for k in tool_stats if k not in VALID_TOOLS]
|
||||||
|
|
||||||
if invalid_tools:
|
if invalid_tools:
|
||||||
filtered_entries += 1
|
filtered_entries += 1
|
||||||
|
|
|
||||||
6
cli.py
6
cli.py
|
|
@ -70,7 +70,7 @@ _COMMAND_SPINNER_FRAMES = ("⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧
|
||||||
|
|
||||||
# Load .env from ~/.hermes/.env first, then project root as dev fallback.
|
# Load .env from ~/.hermes/.env first, then project root as dev fallback.
|
||||||
# User-managed env files should override stale shell exports on restart.
|
# User-managed env files should override stale shell exports on restart.
|
||||||
from hermes_constants import get_hermes_home, display_hermes_home, OPENROUTER_BASE_URL
|
from hermes_constants import get_hermes_home, display_hermes_home
|
||||||
from hermes_cli.env_loader import load_hermes_dotenv
|
from hermes_cli.env_loader import load_hermes_dotenv
|
||||||
|
|
||||||
_hermes_home = get_hermes_home()
|
_hermes_home = get_hermes_home()
|
||||||
|
|
@ -4246,7 +4246,6 @@ class HermesCLI:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config = load_gateway_config()
|
config = load_gateway_config()
|
||||||
connected = config.get_connected_platforms()
|
|
||||||
|
|
||||||
print(" Messaging Platform Configuration:")
|
print(" Messaging Platform Configuration:")
|
||||||
print(" " + "-" * 55)
|
print(" " + "-" * 55)
|
||||||
|
|
@ -6008,7 +6007,7 @@ class HermesCLI:
|
||||||
|
|
||||||
timeout = CLI_CONFIG.get("clarify", {}).get("timeout", 120)
|
timeout = CLI_CONFIG.get("clarify", {}).get("timeout", 120)
|
||||||
response_queue = queue.Queue()
|
response_queue = queue.Queue()
|
||||||
is_open_ended = not choices or len(choices) == 0
|
is_open_ended = not choices
|
||||||
|
|
||||||
self._clarify_state = {
|
self._clarify_state = {
|
||||||
"question": question,
|
"question": question,
|
||||||
|
|
@ -7839,7 +7838,6 @@ class HermesCLI:
|
||||||
title = '🔐 Sudo Password Required'
|
title = '🔐 Sudo Password Required'
|
||||||
body = 'Enter password below (hidden), or press Enter to skip'
|
body = 'Enter password below (hidden), or press Enter to skip'
|
||||||
box_width = _panel_box_width(title, [body])
|
box_width = _panel_box_width(title, [body])
|
||||||
inner = max(0, box_width - 2)
|
|
||||||
lines = []
|
lines = []
|
||||||
lines.append(('class:sudo-border', '╭─ '))
|
lines.append(('class:sudo-border', '╭─ '))
|
||||||
lines.append(('class:sudo-title', title))
|
lines.append(('class:sudo-title', title))
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ except ImportError:
|
||||||
import msvcrt
|
import msvcrt
|
||||||
except ImportError:
|
except ImportError:
|
||||||
msvcrt = None
|
msvcrt = None
|
||||||
import time
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,6 @@ def _build_discord(adapter) -> List[Dict[str, str]]:
|
||||||
|
|
||||||
def _build_slack(adapter) -> List[Dict[str, str]]:
|
def _build_slack(adapter) -> List[Dict[str, str]]:
|
||||||
"""List Slack channels the bot has joined."""
|
"""List Slack channels the bot has joined."""
|
||||||
channels = []
|
|
||||||
# Slack adapter may expose a web client
|
# Slack adapter may expose a web client
|
||||||
client = getattr(adapter, "_app", None) or getattr(adapter, "_client", None)
|
client = getattr(adapter, "_app", None) or getattr(adapter, "_client", None)
|
||||||
if not client:
|
if not client:
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ sys.path.insert(0, str(_Path(__file__).resolve().parents[2]))
|
||||||
|
|
||||||
from gateway.config import Platform, PlatformConfig
|
from gateway.config import Platform, PlatformConfig
|
||||||
from gateway.session import SessionSource, build_session_key
|
from gateway.session import SessionSource, build_session_key
|
||||||
from hermes_cli.config import get_hermes_home
|
|
||||||
from hermes_constants import get_hermes_dir
|
from hermes_constants import get_hermes_dir
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ try:
|
||||||
CreateMessageRequestBody,
|
CreateMessageRequestBody,
|
||||||
GetChatRequest,
|
GetChatRequest,
|
||||||
GetMessageRequest,
|
GetMessageRequest,
|
||||||
GetImageRequest,
|
|
||||||
GetMessageResourceRequest,
|
GetMessageResourceRequest,
|
||||||
P2ImMessageMessageReadV1,
|
P2ImMessageMessageReadV1,
|
||||||
ReplyMessageRequest,
|
ReplyMessageRequest,
|
||||||
|
|
|
||||||
|
|
@ -1057,7 +1057,7 @@ class MatrixAdapter(BasePlatformAdapter):
|
||||||
|
|
||||||
# Message type.
|
# Message type.
|
||||||
msg_type = MessageType.TEXT
|
msg_type = MessageType.TEXT
|
||||||
if body.startswith("!") or body.startswith("/"):
|
if body.startswith(("!", "/")):
|
||||||
msg_type = MessageType.COMMAND
|
msg_type = MessageType.COMMAND
|
||||||
|
|
||||||
source = self.build_source(
|
source = self.build_source(
|
||||||
|
|
|
||||||
|
|
@ -430,7 +430,6 @@ class MattermostAdapter(BasePlatformAdapter):
|
||||||
ct = resp.content_type or "application/octet-stream"
|
ct = resp.content_type or "application/octet-stream"
|
||||||
break
|
break
|
||||||
except (aiohttp.ClientError, asyncio.TimeoutError) as exc:
|
except (aiohttp.ClientError, asyncio.TimeoutError) as exc:
|
||||||
last_exc = exc
|
|
||||||
if attempt < 2:
|
if attempt < 2:
|
||||||
await asyncio.sleep(1.5 * (attempt + 1))
|
await asyncio.sleep(1.5 * (attempt + 1))
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -979,15 +979,6 @@ class SlackAdapter(BasePlatformAdapter):
|
||||||
try:
|
try:
|
||||||
# Build a SessionSource for this thread
|
# Build a SessionSource for this thread
|
||||||
from gateway.session import SessionSource
|
from gateway.session import SessionSource
|
||||||
from gateway.config import Platform
|
|
||||||
|
|
||||||
source = SessionSource(
|
|
||||||
platform=Platform.SLACK,
|
|
||||||
chat_id=channel_id,
|
|
||||||
chat_type="group",
|
|
||||||
user_id=user_id,
|
|
||||||
thread_id=thread_ts,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generate the session key using the same logic as SessionStore
|
# Generate the session key using the same logic as SessionStore
|
||||||
# This mirrors the logic in build_session_key for group sessions
|
# This mirrors the logic in build_session_key for group sessions
|
||||||
|
|
|
||||||
|
|
@ -1369,7 +1369,7 @@ class TelegramAdapter(BasePlatformAdapter):
|
||||||
|
|
||||||
with open(audio_path, "rb") as audio_file:
|
with open(audio_path, "rb") as audio_file:
|
||||||
# .ogg files -> send as voice (round playable bubble)
|
# .ogg files -> send as voice (round playable bubble)
|
||||||
if audio_path.endswith(".ogg") or audio_path.endswith(".opus"):
|
if audio_path.endswith((".ogg", ".opus")):
|
||||||
_voice_thread = metadata.get("thread_id") if metadata else None
|
_voice_thread = metadata.get("thread_id") if metadata else None
|
||||||
msg = await self._bot.send_voice(
|
msg = await self._bot.send_voice(
|
||||||
chat_id=int(chat_id),
|
chat_id=int(chat_id),
|
||||||
|
|
|
||||||
|
|
@ -653,7 +653,7 @@ class WeComAdapter(BasePlatformAdapter):
|
||||||
return ".png"
|
return ".png"
|
||||||
if data.startswith(b"\xff\xd8\xff"):
|
if data.startswith(b"\xff\xd8\xff"):
|
||||||
return ".jpg"
|
return ".jpg"
|
||||||
if data.startswith(b"GIF87a") or data.startswith(b"GIF89a"):
|
if data.startswith((b"GIF87a", b"GIF89a")):
|
||||||
return ".gif"
|
return ".gif"
|
||||||
if data.startswith(b"RIFF") and data[8:12] == b"WEBP":
|
if data.startswith(b"RIFF") and data[8:12] == b"WEBP":
|
||||||
return ".webp"
|
return ".webp"
|
||||||
|
|
@ -689,7 +689,7 @@ class WeComAdapter(BasePlatformAdapter):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _derive_message_type(body: Dict[str, Any], text: str, media_types: List[str]) -> MessageType:
|
def _derive_message_type(body: Dict[str, Any], text: str, media_types: List[str]) -> MessageType:
|
||||||
"""Choose the normalized inbound message type."""
|
"""Choose the normalized inbound message type."""
|
||||||
if any(mtype.startswith("application/") or mtype.startswith("text/") for mtype in media_types):
|
if any(mtype.startswith(("application/", "text/")) for mtype in media_types):
|
||||||
return MessageType.DOCUMENT
|
return MessageType.DOCUMENT
|
||||||
if any(mtype.startswith("image/") for mtype in media_types):
|
if any(mtype.startswith("image/") for mtype in media_types):
|
||||||
return MessageType.TEXT if text else MessageType.PHOTO
|
return MessageType.TEXT if text else MessageType.PHOTO
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ _IS_WINDOWS = platform.system() == "Windows"
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Optional, Any
|
from typing import Dict, Optional, Any
|
||||||
|
|
||||||
from hermes_cli.config import get_hermes_home
|
|
||||||
from hermes_constants import get_hermes_dir
|
from hermes_constants import get_hermes_dir
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import signal
|
||||||
import tempfile
|
import tempfile
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import uuid
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict, Optional, Any, List
|
from typing import Dict, Optional, Any, List
|
||||||
|
|
@ -378,7 +377,7 @@ def _check_unavailable_skill(command_name: str) -> str | None:
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check optional skills (shipped with repo but not installed)
|
# Check optional skills (shipped with repo but not installed)
|
||||||
from hermes_constants import get_hermes_home, get_optional_skills_dir
|
from hermes_constants import get_optional_skills_dir
|
||||||
repo_root = Path(__file__).resolve().parent.parent
|
repo_root = Path(__file__).resolve().parent.parent
|
||||||
optional_dir = get_optional_skills_dir(repo_root / "optional-skills")
|
optional_dir = get_optional_skills_dir(repo_root / "optional-skills")
|
||||||
if optional_dir.exists():
|
if optional_dir.exists():
|
||||||
|
|
@ -2822,7 +2821,7 @@ class GatewayRunner:
|
||||||
guessed, _ = _mimetypes.guess_type(path)
|
guessed, _ = _mimetypes.guess_type(path)
|
||||||
if guessed:
|
if guessed:
|
||||||
mtype = guessed
|
mtype = guessed
|
||||||
if not (mtype.startswith("application/") or mtype.startswith("text/")):
|
if not mtype.startswith(("application/", "text/")):
|
||||||
continue
|
continue
|
||||||
# Extract display filename by stripping the doc_{uuid12}_ prefix
|
# Extract display filename by stripping the doc_{uuid12}_ prefix
|
||||||
import os as _os
|
import os as _os
|
||||||
|
|
@ -3909,7 +3908,7 @@ class GatewayRunner:
|
||||||
|
|
||||||
return f"🎭 Personality set to **{args}**\n_(takes effect on next message)_"
|
return f"🎭 Personality set to **{args}**\n_(takes effect on next message)_"
|
||||||
|
|
||||||
available = "`none`, " + ", ".join(f"`{n}`" for n in personalities.keys())
|
available = "`none`, " + ", ".join(f"`{n}`" for n in personalities)
|
||||||
return f"Unknown personality: `{args}`\n\nAvailable: {available}"
|
return f"Unknown personality: `{args}`\n\nAvailable: {available}"
|
||||||
|
|
||||||
async def _handle_retry_command(self, event: MessageEvent) -> str:
|
async def _handle_retry_command(self, event: MessageEvent) -> str:
|
||||||
|
|
@ -5321,9 +5320,6 @@ class GatewayRunner:
|
||||||
old_servers = set(_servers.keys())
|
old_servers = set(_servers.keys())
|
||||||
|
|
||||||
# Read new config before shutting down, so we know what will be added/removed
|
# Read new config before shutting down, so we know what will be added/removed
|
||||||
new_config = _load_mcp_config()
|
|
||||||
new_server_names = set(new_config.keys())
|
|
||||||
|
|
||||||
# Shutdown existing connections
|
# Shutdown existing connections
|
||||||
await loop.run_in_executor(None, shutdown_mcp_servers)
|
await loop.run_in_executor(None, shutdown_mcp_servers)
|
||||||
|
|
||||||
|
|
@ -5411,7 +5407,6 @@ class GatewayRunner:
|
||||||
|
|
||||||
from tools.approval import (
|
from tools.approval import (
|
||||||
resolve_gateway_approval, has_blocking_approval,
|
resolve_gateway_approval, has_blocking_approval,
|
||||||
pending_approval_count,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if not has_blocking_approval(session_key):
|
if not has_blocking_approval(session_key):
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ class GatewayStreamConsumer:
|
||||||
got_done
|
got_done
|
||||||
or got_segment_break
|
or got_segment_break
|
||||||
or (elapsed >= self.cfg.edit_interval
|
or (elapsed >= self.cfg.edit_interval
|
||||||
and len(self._accumulated) > 0)
|
and self._accumulated)
|
||||||
or len(self._accumulated) >= self.cfg.buffer_threshold
|
or len(self._accumulated) >= self.cfg.buffer_threshold
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2839,7 +2839,6 @@ def _login_nous(args, pconfig: ProviderConfig) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
inference_base_url = auth_state["inference_base_url"]
|
inference_base_url = auth_state["inference_base_url"]
|
||||||
verify: bool | str = False if insecure else (ca_bundle if ca_bundle else True)
|
|
||||||
|
|
||||||
with _auth_store_lock():
|
with _auth_store_lock():
|
||||||
auth_store = _load_auth_store()
|
auth_store = _load_auth_store()
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ from agent.credential_pool import (
|
||||||
STRATEGY_ROUND_ROBIN,
|
STRATEGY_ROUND_ROBIN,
|
||||||
STRATEGY_RANDOM,
|
STRATEGY_RANDOM,
|
||||||
STRATEGY_LEAST_USED,
|
STRATEGY_LEAST_USED,
|
||||||
SUPPORTED_POOL_STRATEGIES,
|
|
||||||
PooledCredential,
|
PooledCredential,
|
||||||
_exhausted_until,
|
_exhausted_until,
|
||||||
_normalize_custom_pool_name,
|
_normalize_custom_pool_name,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ Pure display functions with no HermesCLI state dependency.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ def clarify_callback(cli, question, choices):
|
||||||
|
|
||||||
timeout = CLI_CONFIG.get("clarify", {}).get("timeout", 120)
|
timeout = CLI_CONFIG.get("clarify", {}).get("timeout", 120)
|
||||||
response_queue = queue.Queue()
|
response_queue = queue.Queue()
|
||||||
is_open_ended = not choices or len(choices) == 0
|
is_open_ended = not choices
|
||||||
|
|
||||||
cli._clarify_state = {
|
cli._clarify_state = {
|
||||||
"question": question,
|
"question": question,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ Usage:
|
||||||
|
|
||||||
import importlib.util
|
import importlib.util
|
||||||
import logging
|
import logging
|
||||||
import shutil
|
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
@ -24,7 +23,6 @@ from hermes_cli.setup import (
|
||||||
print_info,
|
print_info,
|
||||||
print_success,
|
print_success,
|
||||||
print_error,
|
print_error,
|
||||||
print_warning,
|
|
||||||
prompt_yes_no,
|
prompt_yes_no,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2520,7 +2520,7 @@ def set_config_value(key: str, value: str):
|
||||||
'TINKER_API_KEY',
|
'TINKER_API_KEY',
|
||||||
]
|
]
|
||||||
|
|
||||||
if key.upper() in api_keys or key.upper().endswith('_API_KEY') or key.upper().endswith('_TOKEN') or key.upper().startswith('TERMINAL_SSH'):
|
if key.upper() in api_keys or key.upper().endswith(('_API_KEY', '_TOKEN')) or key.upper().startswith('TERMINAL_SSH'):
|
||||||
save_env_value(key.upper(), value)
|
save_env_value(key.upper(), value)
|
||||||
print(f"✓ Set {key} in {get_env_path()}")
|
print(f"✓ Set {key} in {get_env_path()}")
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -920,8 +920,8 @@ def run_doctor(args):
|
||||||
pass
|
pass
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
except Exception as _e:
|
except Exception:
|
||||||
logger.debug("Profile health check failed: %s", _e)
|
pass
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# Summary
|
# Summary
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ Usage examples::
|
||||||
hermes logs --since 30m -f # follow, starting 30 min ago
|
hermes logs --since 30m -f # follow, starting 30 min ago
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
|
||||||
|
|
@ -1154,7 +1154,7 @@ def _model_flow_nous(config, current_model="", args=None):
|
||||||
from hermes_cli.auth import (
|
from hermes_cli.auth import (
|
||||||
get_provider_auth_state, _prompt_model_selection, _save_model_choice,
|
get_provider_auth_state, _prompt_model_selection, _save_model_choice,
|
||||||
_update_config_for_provider, resolve_nous_runtime_credentials,
|
_update_config_for_provider, resolve_nous_runtime_credentials,
|
||||||
fetch_nous_models, AuthError, format_auth_error,
|
AuthError, format_auth_error,
|
||||||
_login_nous, PROVIDER_REGISTRY,
|
_login_nous, PROVIDER_REGISTRY,
|
||||||
)
|
)
|
||||||
from hermes_cli.config import get_env_value, save_config, save_env_value
|
from hermes_cli.config import get_env_value, save_config, save_env_value
|
||||||
|
|
@ -1314,7 +1314,6 @@ def _model_flow_openai_codex(config, current_model=""):
|
||||||
PROVIDER_REGISTRY, DEFAULT_CODEX_BASE_URL,
|
PROVIDER_REGISTRY, DEFAULT_CODEX_BASE_URL,
|
||||||
)
|
)
|
||||||
from hermes_cli.codex_models import get_codex_model_ids
|
from hermes_cli.codex_models import get_codex_model_ids
|
||||||
from hermes_cli.config import get_env_value, save_env_value
|
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
status = get_codex_auth_status()
|
status = get_codex_auth_status()
|
||||||
|
|
@ -1367,7 +1366,7 @@ def _model_flow_custom(config):
|
||||||
so it appears in the provider menu on subsequent runs.
|
so it appears in the provider menu on subsequent runs.
|
||||||
"""
|
"""
|
||||||
from hermes_cli.auth import _save_model_choice, deactivate_provider
|
from hermes_cli.auth import _save_model_choice, deactivate_provider
|
||||||
from hermes_cli.config import get_env_value, save_env_value, load_config, save_config
|
from hermes_cli.config import get_env_value, load_config, save_config
|
||||||
|
|
||||||
current_url = get_env_value("OPENAI_BASE_URL") or ""
|
current_url = get_env_value("OPENAI_BASE_URL") or ""
|
||||||
current_key = get_env_value("OPENAI_API_KEY") or ""
|
current_key = get_env_value("OPENAI_API_KEY") or ""
|
||||||
|
|
@ -1629,7 +1628,7 @@ def _model_flow_named_custom(config, provider_info):
|
||||||
Otherwise probes the endpoint's /models API to let the user pick one.
|
Otherwise probes the endpoint's /models API to let the user pick one.
|
||||||
"""
|
"""
|
||||||
from hermes_cli.auth import _save_model_choice, deactivate_provider
|
from hermes_cli.auth import _save_model_choice, deactivate_provider
|
||||||
from hermes_cli.config import save_env_value, load_config, save_config
|
from hermes_cli.config import load_config, save_config
|
||||||
from hermes_cli.models import fetch_api_models
|
from hermes_cli.models import fetch_api_models
|
||||||
|
|
||||||
name = provider_info["name"]
|
name = provider_info["name"]
|
||||||
|
|
@ -1839,7 +1838,7 @@ def _model_flow_copilot(config, current_model=""):
|
||||||
deactivate_provider,
|
deactivate_provider,
|
||||||
resolve_api_key_provider_credentials,
|
resolve_api_key_provider_credentials,
|
||||||
)
|
)
|
||||||
from hermes_cli.config import get_env_value, save_env_value, load_config, save_config
|
from hermes_cli.config import save_env_value, load_config, save_config
|
||||||
from hermes_cli.models import (
|
from hermes_cli.models import (
|
||||||
fetch_api_models,
|
fetch_api_models,
|
||||||
fetch_github_model_catalog,
|
fetch_github_model_catalog,
|
||||||
|
|
@ -2430,8 +2429,6 @@ def _model_flow_anthropic(config, current_model=""):
|
||||||
)
|
)
|
||||||
from hermes_cli.models import _PROVIDER_MODELS
|
from hermes_cli.models import _PROVIDER_MODELS
|
||||||
|
|
||||||
pconfig = PROVIDER_REGISTRY["anthropic"]
|
|
||||||
|
|
||||||
# Check ALL credential sources
|
# Check ALL credential sources
|
||||||
existing_key = (
|
existing_key = (
|
||||||
get_env_value("ANTHROPIC_TOKEN")
|
get_env_value("ANTHROPIC_TOKEN")
|
||||||
|
|
@ -3700,7 +3697,7 @@ def cmd_update(args):
|
||||||
try:
|
try:
|
||||||
from hermes_cli.gateway import (
|
from hermes_cli.gateway import (
|
||||||
is_macos, is_linux, _ensure_user_systemd_env,
|
is_macos, is_linux, _ensure_user_systemd_env,
|
||||||
get_systemd_linger_status, find_gateway_pids,
|
find_gateway_pids,
|
||||||
_get_service_pids,
|
_get_service_pids,
|
||||||
)
|
)
|
||||||
import signal as _signal
|
import signal as _signal
|
||||||
|
|
@ -3856,7 +3853,7 @@ def cmd_profile(args):
|
||||||
"""Profile management — create, delete, list, switch, alias."""
|
"""Profile management — create, delete, list, switch, alias."""
|
||||||
from hermes_cli.profiles import (
|
from hermes_cli.profiles import (
|
||||||
list_profiles, create_profile, delete_profile, seed_profile_skills,
|
list_profiles, create_profile, delete_profile, seed_profile_skills,
|
||||||
get_active_profile, set_active_profile, get_active_profile_name,
|
set_active_profile, get_active_profile_name,
|
||||||
check_alias_collision, create_wrapper_script, remove_wrapper_script,
|
check_alias_collision, create_wrapper_script, remove_wrapper_script,
|
||||||
_is_wrapper_dir_in_path, _get_wrapper_dir,
|
_is_wrapper_dir_in_path, _get_wrapper_dir,
|
||||||
)
|
)
|
||||||
|
|
@ -3984,7 +3981,6 @@ def cmd_profile(args):
|
||||||
print(f" {name} chat Start chatting")
|
print(f" {name} chat Start chatting")
|
||||||
print(f" {name} gateway start Start the messaging gateway")
|
print(f" {name} gateway start Start the messaging gateway")
|
||||||
if clone or clone_all:
|
if clone or clone_all:
|
||||||
from hermes_constants import get_hermes_home
|
|
||||||
profile_dir_display = f"~/.hermes/profiles/{name}"
|
profile_dir_display = f"~/.hermes/profiles/{name}"
|
||||||
print(f"\n Edit {profile_dir_display}/.env for different API keys")
|
print(f"\n Edit {profile_dir_display}/.env for different API keys")
|
||||||
print(f" Edit {profile_dir_display}/SOUL.md for different personality")
|
print(f" Edit {profile_dir_display}/SOUL.md for different personality")
|
||||||
|
|
@ -4407,7 +4403,7 @@ For more help on a command:
|
||||||
gateway_uninstall.add_argument("--system", action="store_true", help="Target the Linux system-level gateway service")
|
gateway_uninstall.add_argument("--system", action="store_true", help="Target the Linux system-level gateway service")
|
||||||
|
|
||||||
# gateway setup
|
# gateway setup
|
||||||
gateway_setup = gateway_subparsers.add_parser("setup", help="Configure messaging platforms")
|
gateway_subparsers.add_parser("setup", help="Configure messaging platforms")
|
||||||
|
|
||||||
gateway_parser.set_defaults(func=cmd_gateway)
|
gateway_parser.set_defaults(func=cmd_gateway)
|
||||||
|
|
||||||
|
|
@ -4682,10 +4678,10 @@ For more help on a command:
|
||||||
config_subparsers = config_parser.add_subparsers(dest="config_command")
|
config_subparsers = config_parser.add_subparsers(dest="config_command")
|
||||||
|
|
||||||
# config show (default)
|
# config show (default)
|
||||||
config_show = config_subparsers.add_parser("show", help="Show current configuration")
|
config_subparsers.add_parser("show", help="Show current configuration")
|
||||||
|
|
||||||
# config edit
|
# config edit
|
||||||
config_edit = config_subparsers.add_parser("edit", help="Open config file in editor")
|
config_subparsers.add_parser("edit", help="Open config file in editor")
|
||||||
|
|
||||||
# config set
|
# config set
|
||||||
config_set = config_subparsers.add_parser("set", help="Set a configuration value")
|
config_set = config_subparsers.add_parser("set", help="Set a configuration value")
|
||||||
|
|
@ -4693,16 +4689,16 @@ For more help on a command:
|
||||||
config_set.add_argument("value", nargs="?", help="Value to set")
|
config_set.add_argument("value", nargs="?", help="Value to set")
|
||||||
|
|
||||||
# config path
|
# config path
|
||||||
config_path = config_subparsers.add_parser("path", help="Print config file path")
|
config_subparsers.add_parser("path", help="Print config file path")
|
||||||
|
|
||||||
# config env-path
|
# config env-path
|
||||||
config_env = config_subparsers.add_parser("env-path", help="Print .env file path")
|
config_subparsers.add_parser("env-path", help="Print .env file path")
|
||||||
|
|
||||||
# config check
|
# config check
|
||||||
config_check = config_subparsers.add_parser("check", help="Check for missing/outdated config")
|
config_subparsers.add_parser("check", help="Check for missing/outdated config")
|
||||||
|
|
||||||
# config migrate
|
# config migrate
|
||||||
config_migrate = config_subparsers.add_parser("migrate", help="Update config with new options")
|
config_subparsers.add_parser("migrate", help="Update config with new options")
|
||||||
|
|
||||||
config_parser.set_defaults(func=cmd_config)
|
config_parser.set_defaults(func=cmd_config)
|
||||||
|
|
||||||
|
|
@ -4716,7 +4712,7 @@ For more help on a command:
|
||||||
)
|
)
|
||||||
pairing_sub = pairing_parser.add_subparsers(dest="pairing_action")
|
pairing_sub = pairing_parser.add_subparsers(dest="pairing_action")
|
||||||
|
|
||||||
pairing_list_parser = pairing_sub.add_parser("list", help="Show pending + approved users")
|
pairing_sub.add_parser("list", help="Show pending + approved users")
|
||||||
|
|
||||||
pairing_approve_parser = pairing_sub.add_parser("approve", help="Approve a pairing code")
|
pairing_approve_parser = pairing_sub.add_parser("approve", help="Approve a pairing code")
|
||||||
pairing_approve_parser.add_argument("platform", help="Platform name (telegram, discord, slack, whatsapp)")
|
pairing_approve_parser.add_argument("platform", help="Platform name (telegram, discord, slack, whatsapp)")
|
||||||
|
|
@ -4726,7 +4722,7 @@ For more help on a command:
|
||||||
pairing_revoke_parser.add_argument("platform", help="Platform name")
|
pairing_revoke_parser.add_argument("platform", help="Platform name")
|
||||||
pairing_revoke_parser.add_argument("user_id", help="User ID to revoke")
|
pairing_revoke_parser.add_argument("user_id", help="User ID to revoke")
|
||||||
|
|
||||||
pairing_clear_parser = pairing_sub.add_parser("clear-pending", help="Clear all pending codes")
|
pairing_sub.add_parser("clear-pending", help="Clear all pending codes")
|
||||||
|
|
||||||
def cmd_pairing(args):
|
def cmd_pairing(args):
|
||||||
from hermes_cli.pairing import pairing_command
|
from hermes_cli.pairing import pairing_command
|
||||||
|
|
@ -4902,7 +4898,7 @@ For more help on a command:
|
||||||
memory_sub = memory_parser.add_subparsers(dest="memory_command")
|
memory_sub = memory_parser.add_subparsers(dest="memory_command")
|
||||||
memory_sub.add_parser("setup", help="Interactive provider selection and configuration")
|
memory_sub.add_parser("setup", help="Interactive provider selection and configuration")
|
||||||
memory_sub.add_parser("status", help="Show current memory provider config")
|
memory_sub.add_parser("status", help="Show current memory provider config")
|
||||||
memory_off_p = memory_sub.add_parser("off", help="Disable external provider (built-in only)")
|
memory_sub.add_parser("off", help="Disable external provider (built-in only)")
|
||||||
|
|
||||||
def cmd_memory(args):
|
def cmd_memory(args):
|
||||||
sub = getattr(args, "memory_command", None)
|
sub = getattr(args, "memory_command", None)
|
||||||
|
|
@ -5066,7 +5062,7 @@ For more help on a command:
|
||||||
sessions_prune.add_argument("--source", help="Only prune sessions from this source")
|
sessions_prune.add_argument("--source", help="Only prune sessions from this source")
|
||||||
sessions_prune.add_argument("--yes", "-y", action="store_true", help="Skip confirmation")
|
sessions_prune.add_argument("--yes", "-y", action="store_true", help="Skip confirmation")
|
||||||
|
|
||||||
sessions_stats = sessions_subparsers.add_parser("stats", help="Show session store statistics")
|
sessions_subparsers.add_parser("stats", help="Show session store statistics")
|
||||||
|
|
||||||
sessions_rename = sessions_subparsers.add_parser("rename", help="Set or change a session's title")
|
sessions_rename = sessions_subparsers.add_parser("rename", help="Set or change a session's title")
|
||||||
sessions_rename.add_argument("session_id", help="Session ID to rename")
|
sessions_rename.add_argument("session_id", help="Session ID to rename")
|
||||||
|
|
@ -5426,7 +5422,7 @@ For more help on a command:
|
||||||
)
|
)
|
||||||
profile_subparsers = profile_parser.add_subparsers(dest="profile_action")
|
profile_subparsers = profile_parser.add_subparsers(dest="profile_action")
|
||||||
|
|
||||||
profile_list = profile_subparsers.add_parser("list", help="List all profiles")
|
profile_subparsers.add_parser("list", help="List all profiles")
|
||||||
profile_use = profile_subparsers.add_parser("use", help="Set sticky default profile")
|
profile_use = profile_subparsers.add_parser("use", help="Set sticky default profile")
|
||||||
profile_use.add_argument("profile_name", help="Profile name (or 'default')")
|
profile_use.add_argument("profile_name", help="Profile name (or 'default')")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,22 +21,16 @@ OpenRouter variant suffixes (``:free``, ``:extended``, ``:fast``).
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass
|
||||||
from typing import List, NamedTuple, Optional
|
from typing import List, NamedTuple, Optional
|
||||||
|
|
||||||
from hermes_cli.providers import (
|
from hermes_cli.providers import (
|
||||||
ALIASES,
|
|
||||||
LABELS,
|
|
||||||
TRANSPORT_TO_API_MODE,
|
|
||||||
determine_api_mode,
|
determine_api_mode,
|
||||||
get_label,
|
get_label,
|
||||||
get_provider,
|
|
||||||
is_aggregator,
|
is_aggregator,
|
||||||
normalize_provider,
|
|
||||||
resolve_provider_full,
|
resolve_provider_full,
|
||||||
)
|
)
|
||||||
from hermes_cli.model_normalize import (
|
from hermes_cli.model_normalize import (
|
||||||
detect_vendor,
|
|
||||||
normalize_model_for_provider,
|
normalize_model_for_provider,
|
||||||
)
|
)
|
||||||
from agent.models_dev import (
|
from agent.models_dev import (
|
||||||
|
|
|
||||||
|
|
@ -294,7 +294,7 @@ def cmd_install(identifier: str, force: bool = False) -> None:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Warn about insecure / local URL schemes
|
# Warn about insecure / local URL schemes
|
||||||
if git_url.startswith("http://") or git_url.startswith("file://"):
|
if git_url.startswith(("http://", "file://")):
|
||||||
console.print(
|
console.print(
|
||||||
"[yellow]Warning:[/yellow] Using insecure/local URL scheme. "
|
"[yellow]Warning:[/yellow] Using insecure/local URL scheme. "
|
||||||
"Consider using https:// or git@ for production installs."
|
"Consider using https:// or git@ for production installs."
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import shutil
|
||||||
import stat
|
import stat
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass
|
||||||
from pathlib import Path, PurePosixPath, PureWindowsPath
|
from pathlib import Path, PurePosixPath, PureWindowsPath
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
|
|
@ -517,7 +517,6 @@ def delete_profile(name: str, yes: bool = False) -> Path:
|
||||||
]
|
]
|
||||||
|
|
||||||
# Check for service
|
# Check for service
|
||||||
from hermes_cli.gateway import _profile_suffix, get_service_name
|
|
||||||
wrapper_path = _get_wrapper_dir() / name
|
wrapper_path = _get_wrapper_dir() / name
|
||||||
has_wrapper = wrapper_path.exists()
|
has_wrapper = wrapper_path.exists()
|
||||||
if has_wrapper:
|
if has_wrapper:
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,7 @@ Other modules import from this file. No parallel registries.
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
from dataclasses import dataclass
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
@ -357,14 +356,6 @@ def _build_labels() -> Dict[str, str]:
|
||||||
# Lazy-built on first access
|
# Lazy-built on first access
|
||||||
_labels_cache: Optional[Dict[str, str]] = None
|
_labels_cache: Optional[Dict[str, str]] = None
|
||||||
|
|
||||||
@property
|
|
||||||
def LABELS() -> Dict[str, str]:
|
|
||||||
"""Backward-compatible labels dict."""
|
|
||||||
global _labels_cache
|
|
||||||
if _labels_cache is None:
|
|
||||||
_labels_cache = _build_labels()
|
|
||||||
return _labels_cache
|
|
||||||
|
|
||||||
# For direct import compat, expose as module-level dict
|
# For direct import compat, expose as module-level dict
|
||||||
# Built on demand by get_label() calls
|
# Built on demand by get_label() calls
|
||||||
LABELS: Dict[str, str] = {
|
LABELS: Dict[str, str] = {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ from typing import Optional, Dict, Any
|
||||||
|
|
||||||
from hermes_cli.nous_subscription import (
|
from hermes_cli.nous_subscription import (
|
||||||
apply_nous_provider_defaults,
|
apply_nous_provider_defaults,
|
||||||
get_nous_subscription_explainer_lines,
|
|
||||||
get_nous_subscription_features,
|
get_nous_subscription_features,
|
||||||
)
|
)
|
||||||
from tools.tool_backend_helpers import managed_nous_tools_enabled
|
from tools.tool_backend_helpers import managed_nous_tools_enabled
|
||||||
|
|
@ -1348,8 +1347,6 @@ def setup_terminal_backend(config: dict):
|
||||||
terminal_choices.append(f"Keep current ({current_backend})")
|
terminal_choices.append(f"Keep current ({current_backend})")
|
||||||
idx_to_backend[keep_current_idx] = current_backend
|
idx_to_backend[keep_current_idx] = current_backend
|
||||||
|
|
||||||
default_terminal = backend_to_idx.get(current_backend, 0)
|
|
||||||
|
|
||||||
terminal_idx = prompt_choice(
|
terminal_idx = prompt_choice(
|
||||||
"Select terminal backend:", terminal_choices, keep_current_idx
|
"Select terminal backend:", terminal_choices, keep_current_idx
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,6 @@ Activate with ``/skin <name>`` in the CLI or ``display.skin: <name>`` in config.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ Provides options for:
|
||||||
- Keep data: Remove code but keep ~/.hermes/ (configs, sessions, logs)
|
- Keep data: Remove code but keep ~/.hermes/ (configs, sessions, logs)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import re
|
||||||
import secrets
|
import secrets
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Optional
|
from typing import Dict
|
||||||
|
|
||||||
from hermes_constants import display_hermes_home
|
from hermes_constants import display_hermes_home
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ secrets are never written to disk.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ Key design decisions:
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ crashes due to a bad timezone string.
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_constants import get_hermes_home
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
@ -92,7 +91,6 @@ def get_timezone() -> Optional[ZoneInfo]:
|
||||||
|
|
||||||
def get_timezone_name() -> str:
|
def get_timezone_name() -> str:
|
||||||
"""Return the IANA name of the configured timezone, or empty string."""
|
"""Return the IANA name of the configured timezone, or empty string."""
|
||||||
global _cached_tz_name, _cache_resolved
|
|
||||||
if not _cache_resolved:
|
if not _cache_resolved:
|
||||||
get_timezone() # populates cache
|
get_timezone() # populates cache
|
||||||
return _cached_tz_name or ""
|
return _cached_tz_name or ""
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,8 @@ import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import datetime
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
logger = logging.getLogger("hermes.mcp_serve")
|
logger = logging.getLogger("hermes.mcp_serve")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import os
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
import time
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ from __future__ import annotations
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from agent.memory_provider import MemoryProvider
|
from agent.memory_provider import MemoryProvider
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ Single-user Hermes memory store plugin.
|
||||||
import re
|
import re
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import threading
|
import threading
|
||||||
from datetime import datetime
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ from __future__ import annotations
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from agent.memory_provider import MemoryProvider
|
from agent.memory_provider import MemoryProvider
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_constants import get_hermes_home
|
||||||
from plugins.memory.honcho.client import resolve_active_host, resolve_config_path, GLOBAL_CONFIG_PATH, HOST
|
from plugins.memory.honcho.client import resolve_active_host, resolve_config_path, HOST
|
||||||
|
|
||||||
|
|
||||||
def clone_honcho_for_profile(profile_name: str) -> bool:
|
def clone_honcho_for_profile(profile_name: str) -> bool:
|
||||||
|
|
@ -1220,7 +1220,6 @@ def register_cli(subparser) -> None:
|
||||||
Called by the plugin CLI registration system during argparse setup.
|
Called by the plugin CLI registration system during argparse setup.
|
||||||
The *subparser* is the parser for ``hermes honcho``.
|
The *subparser* is the parser for ``hermes honcho``.
|
||||||
"""
|
"""
|
||||||
import argparse
|
|
||||||
|
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
"--target-profile", metavar="NAME", dest="target_profile",
|
"--target-profile", metavar="NAME", dest="target_profile",
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import logging
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from agent.memory_provider import MemoryProvider
|
from agent.memory_provider import MemoryProvider
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ Config (env vars or hermes config.yaml under retaindb:):
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import hashlib
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
@ -189,7 +188,7 @@ class _Client:
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"x-sdk-runtime": "hermes-plugin",
|
"x-sdk-runtime": "hermes-plugin",
|
||||||
}
|
}
|
||||||
if path.startswith("/v1/memory") or path.startswith("/v1/context"):
|
if path.startswith(("/v1/memory", "/v1/context")):
|
||||||
h["X-API-Key"] = token
|
h["X-API-Key"] = token
|
||||||
return h
|
return h
|
||||||
|
|
||||||
|
|
|
||||||
30
run_agent.py
30
run_agent.py
|
|
@ -20,7 +20,6 @@ Usage:
|
||||||
response = agent.run_conversation("Tell me about the latest Python updates")
|
response = agent.run_conversation("Tell me about the latest Python updates")
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import atexit
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import base64
|
import base64
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
|
|
@ -36,7 +35,6 @@ import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
import weakref
|
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
import uuid
|
import uuid
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any, Optional
|
||||||
|
|
@ -654,7 +652,7 @@ class AIAgent:
|
||||||
self.stream_delta_callback = stream_delta_callback
|
self.stream_delta_callback = stream_delta_callback
|
||||||
self.status_callback = status_callback
|
self.status_callback = status_callback
|
||||||
self.tool_gen_callback = tool_gen_callback
|
self.tool_gen_callback = tool_gen_callback
|
||||||
self._last_reported_tool = None # Track for "new tool" mode
|
|
||||||
|
|
||||||
# Tool execution state — allows _vprint during tool execution
|
# Tool execution state — allows _vprint during tool execution
|
||||||
# even when stream consumers are registered (no tokens streaming then)
|
# even when stream consumers are registered (no tokens streaming then)
|
||||||
|
|
@ -2702,20 +2700,7 @@ class AIAgent:
|
||||||
|
|
||||||
if not _soul_loaded:
|
if not _soul_loaded:
|
||||||
# Fallback to hardcoded identity
|
# Fallback to hardcoded identity
|
||||||
_ai_peer_name = (
|
prompt_parts = [DEFAULT_AGENT_IDENTITY]
|
||||||
None
|
|
||||||
if False
|
|
||||||
else None
|
|
||||||
)
|
|
||||||
if _ai_peer_name:
|
|
||||||
_identity = DEFAULT_AGENT_IDENTITY.replace(
|
|
||||||
"You are Hermes Agent",
|
|
||||||
f"You are {_ai_peer_name}",
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
_identity = DEFAULT_AGENT_IDENTITY
|
|
||||||
prompt_parts = [_identity]
|
|
||||||
|
|
||||||
# Tool-aware behavioral guidance: only inject when the tools are loaded
|
# Tool-aware behavioral guidance: only inject when the tools are loaded
|
||||||
tool_guidance = []
|
tool_guidance = []
|
||||||
|
|
@ -3400,7 +3385,7 @@ class AIAgent:
|
||||||
elif "stream" in api_kwargs:
|
elif "stream" in api_kwargs:
|
||||||
raise ValueError("Codex Responses stream flag is only allowed in fallback streaming requests.")
|
raise ValueError("Codex Responses stream flag is only allowed in fallback streaming requests.")
|
||||||
|
|
||||||
unexpected = sorted(key for key in api_kwargs.keys() if key not in allowed_keys)
|
unexpected = sorted(key for key in api_kwargs if key not in allowed_keys)
|
||||||
if unexpected:
|
if unexpected:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Codex Responses request has unsupported field(s): {', '.join(unexpected)}."
|
f"Codex Responses request has unsupported field(s): {', '.join(unexpected)}."
|
||||||
|
|
@ -5908,7 +5893,7 @@ class AIAgent:
|
||||||
args = json.loads(tc.function.arguments)
|
args = json.loads(tc.function.arguments)
|
||||||
flush_target = args.get("target", "memory")
|
flush_target = args.get("target", "memory")
|
||||||
from tools.memory_tool import memory_tool as _memory_tool
|
from tools.memory_tool import memory_tool as _memory_tool
|
||||||
result = _memory_tool(
|
_memory_tool(
|
||||||
action=args.get("action"),
|
action=args.get("action"),
|
||||||
target=flush_target,
|
target=flush_target,
|
||||||
content=args.get("content"),
|
content=args.get("content"),
|
||||||
|
|
@ -7468,7 +7453,7 @@ class AIAgent:
|
||||||
elif not isinstance(output_items, list):
|
elif not isinstance(output_items, list):
|
||||||
response_invalid = True
|
response_invalid = True
|
||||||
error_details.append("response.output is not a list")
|
error_details.append("response.output is not a list")
|
||||||
elif len(output_items) == 0:
|
elif not output_items:
|
||||||
# If we reach here, _run_codex_stream's backfill
|
# If we reach here, _run_codex_stream's backfill
|
||||||
# from output_item.done events and text-delta
|
# from output_item.done events and text-delta
|
||||||
# synthesis both failed to populate output.
|
# synthesis both failed to populate output.
|
||||||
|
|
@ -7491,11 +7476,11 @@ class AIAgent:
|
||||||
elif not isinstance(content_blocks, list):
|
elif not isinstance(content_blocks, list):
|
||||||
response_invalid = True
|
response_invalid = True
|
||||||
error_details.append("response.content is not a list")
|
error_details.append("response.content is not a list")
|
||||||
elif len(content_blocks) == 0:
|
elif not content_blocks:
|
||||||
response_invalid = True
|
response_invalid = True
|
||||||
error_details.append("response.content is empty")
|
error_details.append("response.content is empty")
|
||||||
else:
|
else:
|
||||||
if response is None or not hasattr(response, 'choices') or response.choices is None or len(response.choices) == 0:
|
if response is None or not hasattr(response, 'choices') or response.choices is None or not response.choices:
|
||||||
response_invalid = True
|
response_invalid = True
|
||||||
if response is None:
|
if response is None:
|
||||||
error_details.append("response is None")
|
error_details.append("response is None")
|
||||||
|
|
@ -9033,7 +9018,6 @@ class AIAgent:
|
||||||
"content": f"Error executing tool: {error_msg}",
|
"content": f"Error executing tool: {error_msg}",
|
||||||
}
|
}
|
||||||
messages.append(err_msg)
|
messages.append(err_msg)
|
||||||
pending_handled = True
|
|
||||||
break
|
break
|
||||||
|
|
||||||
# Non-tool errors don't need a synthetic message injected.
|
# Non-tool errors don't need a synthetic message injected.
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,6 @@ Usage:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ Usage:
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
import os
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Dict, Any, Tuple
|
from typing import List, Dict, Any, Tuple
|
||||||
import fire
|
import fire
|
||||||
|
|
@ -138,7 +137,6 @@ def sample_from_datasets(
|
||||||
List of sampled trajectory entries
|
List of sampled trajectory entries
|
||||||
"""
|
"""
|
||||||
from multiprocessing import Pool
|
from multiprocessing import Pool
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
random.seed(seed)
|
random.seed(seed)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ Adapt this for your specific task by modifying:
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
import re
|
import re
|
||||||
from datasets import load_dataset, Dataset
|
from datasets import load_dataset
|
||||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||||
from peft import LoraConfig
|
from peft import LoraConfig
|
||||||
from trl import GRPOTrainer, GRPOConfig
|
from trl import GRPOTrainer, GRPOConfig
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,10 @@ Usage in execute_code:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import re
|
|
||||||
import yaml
|
import yaml
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ Usage in execute_code:
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import json
|
|
||||||
import time
|
import time
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
|
||||||
|
|
@ -404,7 +403,6 @@ def race_godmode_classic(query, api_key=None, timeout=60):
|
||||||
Each combo uses a different model paired with its best-performing jailbreak prompt.
|
Each combo uses a different model paired with its best-performing jailbreak prompt.
|
||||||
Returns the best result across all combos.
|
Returns the best result across all combos.
|
||||||
"""
|
"""
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
HALL_OF_FAME = [
|
HALL_OF_FAME = [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ Usage:
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import base64
|
import base64
|
||||||
import sys
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════
|
||||||
# Trigger words that commonly trip safety classifiers
|
# Trigger words that commonly trip safety classifiers
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
import time
|
|
||||||
import uuid
|
import uuid
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
@ -445,7 +443,7 @@ def camofox_get_images(task_id: Optional[str] = None) -> str:
|
||||||
lines = snapshot.split("\n")
|
lines = snapshot.split("\n")
|
||||||
for i, line in enumerate(lines):
|
for i, line in enumerate(lines):
|
||||||
stripped = line.strip()
|
stripped = line.strip()
|
||||||
if stripped.startswith("- img ") or stripped.startswith("img "):
|
if stripped.startswith(("- img ", "img ")):
|
||||||
alt_match = re.search(r'img\s+"([^"]*)"', stripped)
|
alt_match = re.search(r'img\s+"([^"]*)"', stripped)
|
||||||
alt = alt_match.group(1) if alt_match else ""
|
alt = alt_match.group(1) if alt_match else ""
|
||||||
# Look for URL on the next line
|
# Look for URL on the next line
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,7 @@ def _resolve_cdp_override(cdp_url: str) -> str:
|
||||||
return raw
|
return raw
|
||||||
|
|
||||||
discovery_url = raw
|
discovery_url = raw
|
||||||
if lowered.startswith("ws://") or lowered.startswith("wss://"):
|
if lowered.startswith(("ws://", "wss://")):
|
||||||
if raw.count(":") == 2 and raw.rstrip("/").rsplit(":", 1)[-1].isdigit() and "/" not in raw.split(":", 2)[-1]:
|
if raw.count(":") == 2 and raw.rstrip("/").rsplit(":", 1)[-1].isdigit() and "/" not in raw.split(":", 2)[-1]:
|
||||||
discovery_url = ("http://" if lowered.startswith("ws://") else "https://") + raw.split("://", 1)[1]
|
discovery_url = ("http://" if lowered.startswith("ws://") else "https://") + raw.split("://", 1)[1]
|
||||||
else:
|
else:
|
||||||
|
|
@ -458,8 +458,6 @@ def _browser_cleanup_thread_worker():
|
||||||
Runs every 30 seconds and checks for sessions that haven't been used
|
Runs every 30 seconds and checks for sessions that haven't been used
|
||||||
within the BROWSER_SESSION_INACTIVITY_TIMEOUT period.
|
within the BROWSER_SESSION_INACTIVITY_TIMEOUT period.
|
||||||
"""
|
"""
|
||||||
global _cleanup_running
|
|
||||||
|
|
||||||
while _cleanup_running:
|
while _cleanup_running:
|
||||||
try:
|
try:
|
||||||
_cleanup_inactive_browser_sessions()
|
_cleanup_inactive_browser_sessions()
|
||||||
|
|
|
||||||
|
|
@ -693,7 +693,6 @@ def _execute_remote(
|
||||||
the remote environment, and tool calls are proxied through a polling
|
the remote environment, and tool calls are proxied through a polling
|
||||||
thread that communicates via request/response files.
|
thread that communicates via request/response files.
|
||||||
"""
|
"""
|
||||||
from tools.terminal_tool import _interrupt_event
|
|
||||||
|
|
||||||
_cfg = _load_config()
|
_cfg = _load_config()
|
||||||
timeout = _cfg.get("timeout", DEFAULT_TIMEOUT)
|
timeout = _cfg.get("timeout", DEFAULT_TIMEOUT)
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,6 @@ def _validate_cron_script_path(script: Optional[str]) -> Optional[str]:
|
||||||
if not script or not script.strip():
|
if not script or not script.strip():
|
||||||
return None # empty/None = clearing the field, always OK
|
return None # empty/None = clearing the field, always OK
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_constants import get_hermes_home
|
||||||
|
|
||||||
raw = script.strip()
|
raw = script.strip()
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_constants import get_hermes_home
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ import shlex
|
||||||
import threading
|
import threading
|
||||||
import uuid
|
import uuid
|
||||||
import warnings
|
import warnings
|
||||||
from typing import Optional
|
from pathlib import Path
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from tools.environments.base import BaseEnvironment
|
from tools.environments.base import BaseEnvironment
|
||||||
from tools.interrupt import is_interrupted
|
from tools.interrupt import is_interrupted
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,10 @@ import os
|
||||||
import shlex
|
import shlex
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
|
||||||
import threading
|
import threading
|
||||||
import uuid
|
import uuid
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_constants import get_hermes_home
|
||||||
from tools.environments.base import BaseEnvironment
|
from tools.environments.base import BaseEnvironment
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ import threading
|
||||||
import webbrowser
|
import webbrowser
|
||||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Optional
|
from typing import Any
|
||||||
from urllib.parse import parse_qs, urlparse
|
from urllib.parse import parse_qs, urlparse
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
@ -54,7 +54,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_OAUTH_AVAILABLE = False
|
_OAUTH_AVAILABLE = False
|
||||||
try:
|
try:
|
||||||
from mcp.client.auth import OAuthClientProvider, TokenStorage
|
from mcp.client.auth import OAuthClientProvider
|
||||||
from mcp.shared.auth import (
|
from mcp.shared.auth import (
|
||||||
OAuthClientInformationFull,
|
OAuthClientInformationFull,
|
||||||
OAuthClientMetadata,
|
OAuthClientMetadata,
|
||||||
|
|
@ -320,7 +320,6 @@ async def _wait_for_callback() -> tuple[str, str | None]:
|
||||||
OAuthNonInteractiveError: If the callback times out (no user present
|
OAuthNonInteractiveError: If the callback times out (no user present
|
||||||
to complete the browser auth).
|
to complete the browser auth).
|
||||||
"""
|
"""
|
||||||
global _oauth_port
|
|
||||||
assert _oauth_port is not None, "OAuth callback port not set"
|
assert _oauth_port is not None, "OAuth callback port not set"
|
||||||
|
|
||||||
# The callback server is already running (started in build_oauth_auth).
|
# The callback server is already running (started in build_oauth_auth).
|
||||||
|
|
|
||||||
|
|
@ -260,7 +260,7 @@ class MemoryStore:
|
||||||
entries = self._entries_for(target)
|
entries = self._entries_for(target)
|
||||||
matches = [(i, e) for i, e in enumerate(entries) if old_text in e]
|
matches = [(i, e) for i, e in enumerate(entries) if old_text in e]
|
||||||
|
|
||||||
if len(matches) == 0:
|
if not matches:
|
||||||
return {"success": False, "error": f"No entry matched '{old_text}'."}
|
return {"success": False, "error": f"No entry matched '{old_text}'."}
|
||||||
|
|
||||||
if len(matches) > 1:
|
if len(matches) > 1:
|
||||||
|
|
@ -310,7 +310,7 @@ class MemoryStore:
|
||||||
entries = self._entries_for(target)
|
entries = self._entries_for(target)
|
||||||
matches = [(i, e) for i, e in enumerate(entries) if old_text in e]
|
matches = [(i, e) for i, e in enumerate(entries) if old_text in e]
|
||||||
|
|
||||||
if len(matches) == 0:
|
if not matches:
|
||||||
return {"success": False, "error": f"No entry matched '{old_text}'."}
|
return {"success": False, "error": f"No entry matched '{old_text}'."}
|
||||||
|
|
||||||
if len(matches) > 1:
|
if len(matches) > 1:
|
||||||
|
|
|
||||||
|
|
@ -567,7 +567,7 @@ async def rl_select_environment(name: str) -> str:
|
||||||
|
|
||||||
TIP: Read the returned file_path to understand how the environment works.
|
TIP: Read the returned file_path to understand how the environment works.
|
||||||
"""
|
"""
|
||||||
global _current_env, _current_config, _env_config_cache
|
global _current_env, _current_config
|
||||||
|
|
||||||
_initialize_environments()
|
_initialize_environments()
|
||||||
|
|
||||||
|
|
@ -673,8 +673,6 @@ async def rl_edit_config(field: str, value: Any) -> str:
|
||||||
Returns:
|
Returns:
|
||||||
JSON string with updated config or error message
|
JSON string with updated config or error message
|
||||||
"""
|
"""
|
||||||
global _current_config
|
|
||||||
|
|
||||||
if not _current_env:
|
if not _current_env:
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
"error": "No environment selected. Use rl_select_environment(name) first.",
|
"error": "No environment selected. Use rl_select_environment(name) first.",
|
||||||
|
|
@ -727,8 +725,6 @@ async def rl_start_training() -> str:
|
||||||
Returns:
|
Returns:
|
||||||
JSON string with run_id and initial status
|
JSON string with run_id and initial status
|
||||||
"""
|
"""
|
||||||
global _active_runs
|
|
||||||
|
|
||||||
if not _current_env:
|
if not _current_env:
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
"error": "No environment selected. Use rl_select_environment(name) first.",
|
"error": "No environment selected. Use rl_select_environment(name) first.",
|
||||||
|
|
@ -829,8 +825,6 @@ async def rl_check_status(run_id: str) -> str:
|
||||||
Returns:
|
Returns:
|
||||||
JSON string with run status and metrics
|
JSON string with run status and metrics
|
||||||
"""
|
"""
|
||||||
global _last_status_check
|
|
||||||
|
|
||||||
# Check rate limiting
|
# Check rate limiting
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if run_id in _last_status_check:
|
if run_id in _last_status_check:
|
||||||
|
|
@ -1311,7 +1305,7 @@ async def rl_test_inference(
|
||||||
"avg_accuracy": round(
|
"avg_accuracy": round(
|
||||||
sum(m.get("accuracy", 0) for m in working_models) / len(working_models), 3
|
sum(m.get("accuracy", 0) for m in working_models) / len(working_models), 3
|
||||||
) if working_models else 0,
|
) if working_models else 0,
|
||||||
"environment_working": len(working_models) > 0,
|
"environment_working": bool(working_models),
|
||||||
"output_directory": str(test_output_dir),
|
"output_directory": str(test_output_dir),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -432,7 +432,7 @@ async def _send_telegram(token, chat_id, message, media_files=None, thread_id=No
|
||||||
else:
|
else:
|
||||||
# Reuse the gateway adapter's format_message for markdown→MarkdownV2
|
# Reuse the gateway adapter's format_message for markdown→MarkdownV2
|
||||||
try:
|
try:
|
||||||
from gateway.platforms.telegram import TelegramAdapter, _strip_mdv2
|
from gateway.platforms.telegram import TelegramAdapter
|
||||||
_adapter = TelegramAdapter.__new__(TelegramAdapter)
|
_adapter = TelegramAdapter.__new__(TelegramAdapter)
|
||||||
formatted = _adapter.format_message(message)
|
formatted = _adapter.format_message(message)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
|
||||||
|
|
@ -430,7 +430,7 @@ class GitHubSource(SkillSource):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
dir_name = entry["name"]
|
dir_name = entry["name"]
|
||||||
if dir_name.startswith(".") or dir_name.startswith("_"):
|
if dir_name.startswith((".", "_")):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
prefix = path.rstrip("/")
|
prefix = path.rstrip("/")
|
||||||
|
|
@ -1163,7 +1163,7 @@ class SkillsShSource(SkillSource):
|
||||||
if entry.get("type") != "dir":
|
if entry.get("type") != "dir":
|
||||||
continue
|
continue
|
||||||
dir_name = entry["name"]
|
dir_name = entry["name"]
|
||||||
if dir_name.startswith(".") or dir_name.startswith("_"):
|
if dir_name.startswith((".", "_")):
|
||||||
continue
|
continue
|
||||||
if dir_name in ("skills", ".agents", ".claude"):
|
if dir_name in ("skills", ".agents", ".claude"):
|
||||||
continue # already tried
|
continue # already tried
|
||||||
|
|
@ -1382,7 +1382,7 @@ class ClawHubSource(SkillSource):
|
||||||
if isinstance(tags, list):
|
if isinstance(tags, list):
|
||||||
return [str(t) for t in tags]
|
return [str(t) for t in tags]
|
||||||
if isinstance(tags, dict):
|
if isinstance(tags, dict):
|
||||||
return [str(k) for k in tags.keys() if str(k) != "latest"]
|
return [str(k) for k in tags if str(k) != "latest"]
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
||||||
|
|
@ -72,12 +72,10 @@ import logging
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_constants import get_hermes_home
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, List, Optional, Set, Tuple
|
from typing import Dict, Any, List, Optional, Set, Tuple
|
||||||
|
|
||||||
import yaml
|
|
||||||
from tools.registry import registry
|
from tools.registry import registry
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
|
||||||
|
|
@ -720,8 +720,6 @@ def _create_environment(env_type: str, image: str, cwd: str, timeout: int,
|
||||||
|
|
||||||
def _cleanup_inactive_envs(lifetime_seconds: int = 300):
|
def _cleanup_inactive_envs(lifetime_seconds: int = 300):
|
||||||
"""Clean up environments that have been inactive for longer than lifetime_seconds."""
|
"""Clean up environments that have been inactive for longer than lifetime_seconds."""
|
||||||
global _active_environments, _last_activity
|
|
||||||
|
|
||||||
current_time = time.time()
|
current_time = time.time()
|
||||||
|
|
||||||
# Check the process registry -- skip cleanup for sandboxes with active
|
# Check the process registry -- skip cleanup for sandboxes with active
|
||||||
|
|
@ -784,8 +782,6 @@ def _cleanup_inactive_envs(lifetime_seconds: int = 300):
|
||||||
|
|
||||||
def _cleanup_thread_worker():
|
def _cleanup_thread_worker():
|
||||||
"""Background thread worker that periodically cleans up inactive environments."""
|
"""Background thread worker that periodically cleans up inactive environments."""
|
||||||
global _cleanup_running
|
|
||||||
|
|
||||||
while _cleanup_running:
|
while _cleanup_running:
|
||||||
try:
|
try:
|
||||||
config = _get_env_config()
|
config = _get_env_config()
|
||||||
|
|
@ -831,7 +827,7 @@ def get_active_environments_info() -> Dict[str, Any]:
|
||||||
|
|
||||||
# Calculate total disk usage (per-task to avoid double-counting)
|
# Calculate total disk usage (per-task to avoid double-counting)
|
||||||
total_size = 0
|
total_size = 0
|
||||||
for task_id in _active_environments.keys():
|
for task_id in _active_environments:
|
||||||
scratch_dir = _get_scratch_dir()
|
scratch_dir = _get_scratch_dir()
|
||||||
pattern = f"hermes-*{task_id[:8]}*"
|
pattern = f"hermes-*{task_id[:8]}*"
|
||||||
import glob
|
import glob
|
||||||
|
|
@ -848,8 +844,6 @@ def get_active_environments_info() -> Dict[str, Any]:
|
||||||
|
|
||||||
def cleanup_all_environments():
|
def cleanup_all_environments():
|
||||||
"""Clean up ALL active environments. Use with caution."""
|
"""Clean up ALL active environments. Use with caution."""
|
||||||
global _active_environments, _last_activity
|
|
||||||
|
|
||||||
task_ids = list(_active_environments.keys())
|
task_ids = list(_active_environments.keys())
|
||||||
cleaned = 0
|
cleaned = 0
|
||||||
|
|
||||||
|
|
@ -877,8 +871,6 @@ def cleanup_all_environments():
|
||||||
|
|
||||||
def cleanup_vm(task_id: str):
|
def cleanup_vm(task_id: str):
|
||||||
"""Manually clean up a specific environment by task_id."""
|
"""Manually clean up a specific environment by task_id."""
|
||||||
global _active_environments, _last_activity
|
|
||||||
|
|
||||||
# Remove from tracking dicts while holding the lock, but defer the
|
# Remove from tracking dicts while holding the lock, but defer the
|
||||||
# actual (potentially slow) env.cleanup() call to outside the lock
|
# actual (potentially slow) env.cleanup() call to outside the lock
|
||||||
# so other tool calls aren't blocked.
|
# so other tool calls aren't blocked.
|
||||||
|
|
@ -1043,8 +1035,6 @@ def terminal_tool(
|
||||||
# Force run after user confirmation
|
# Force run after user confirmation
|
||||||
# Note: force parameter is internal only, not exposed to model API
|
# Note: force parameter is internal only, not exposed to model API
|
||||||
"""
|
"""
|
||||||
global _active_environments, _last_activity
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get configuration
|
# Get configuration
|
||||||
config = _get_env_config()
|
config = _get_env_config()
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ class TodoStore:
|
||||||
|
|
||||||
def has_items(self) -> bool:
|
def has_items(self) -> bool:
|
||||||
"""Check if there are any items in the list."""
|
"""Check if there are any items in the list."""
|
||||||
return len(self._items) > 0
|
return bool(self._items)
|
||||||
|
|
||||||
def format_for_injection(self) -> Optional[str]:
|
def format_for_injection(self) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -550,7 +550,6 @@ def text_to_speech_tool(
|
||||||
if edge_available:
|
if edge_available:
|
||||||
logger.info("Generating speech with Edge TTS...")
|
logger.info("Generating speech with Edge TTS...")
|
||||||
try:
|
try:
|
||||||
loop = asyncio.get_running_loop()
|
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
||||||
pool.submit(
|
pool.submit(
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ def _validate_image_url(url: str) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Basic HTTP/HTTPS URL check
|
# Basic HTTP/HTTPS URL check
|
||||||
if not (url.startswith("http://") or url.startswith("https://")):
|
if not url.startswith(("http://", "https://")):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Parse to ensure we at least have a network location; still allow URLs
|
# Parse to ensure we at least have a network location; still allow URLs
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ def detect_audio_environment() -> dict:
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"available": len(warnings) == 0,
|
"available": not warnings,
|
||||||
"warnings": warnings,
|
"warnings": warnings,
|
||||||
"notices": notices,
|
"notices": notices,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ from __future__ import annotations
|
||||||
|
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
|
||||||
|
|
@ -592,7 +592,7 @@ def get_toolset_info(name: str) -> Dict[str, Any]:
|
||||||
"includes": toolset["includes"],
|
"includes": toolset["includes"],
|
||||||
"resolved_tools": resolved_tools,
|
"resolved_tools": resolved_tools,
|
||||||
"tool_count": len(resolved_tools),
|
"tool_count": len(resolved_tools),
|
||||||
"is_composite": len(toolset["includes"]) > 0
|
"is_composite": bool(toolset["includes"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ Usage:
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import time
|
import time
|
||||||
import yaml
|
import yaml
|
||||||
import logging
|
import logging
|
||||||
|
|
@ -350,7 +349,6 @@ class TrajectoryCompressor:
|
||||||
which handles auth, headers, and provider detection internally.
|
which handles auth, headers, and provider detection internally.
|
||||||
For custom endpoints, falls back to raw client construction.
|
For custom endpoints, falls back to raw client construction.
|
||||||
"""
|
"""
|
||||||
from agent.auxiliary_client import call_llm, async_call_llm
|
|
||||||
|
|
||||||
provider = self._detect_provider()
|
provider = self._detect_provider()
|
||||||
if provider:
|
if provider:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue