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:
Teknium 2026-04-07 10:25:31 -07:00 committed by GitHub
parent afe6c63c52
commit d0ffb111c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
80 changed files with 81 additions and 210 deletions

View file

@ -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

View file

@ -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")

View file

@ -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": {

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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__)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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
View file

@ -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))

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -60,7 +60,6 @@ try:
CreateMessageRequestBody, CreateMessageRequestBody,
GetChatRequest, GetChatRequest,
GetMessageRequest, GetMessageRequest,
GetImageRequest,
GetMessageResourceRequest, GetMessageResourceRequest,
P2ImMessageMessageReadV1, P2ImMessageMessageReadV1,
ReplyMessageRequest, ReplyMessageRequest,

View file

@ -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(

View file

@ -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

View file

@ -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

View file

@ -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),

View file

@ -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

View file

@ -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__)

View file

@ -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):

View file

@ -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
) )

View file

@ -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()

View file

@ -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,

View file

@ -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

View file

@ -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,

View file

@ -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,
) )

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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')")

View file

@ -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 (

View file

@ -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."

View file

@ -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:

View file

@ -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] = {

View file

@ -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
) )

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 ""

View file

@ -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")

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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",

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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 = [
{ {

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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)

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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).

View file

@ -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:

View file

@ -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),
} }

View file

@ -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:

View file

@ -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

View file

@ -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__)

View file

@ -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()

View file

@ -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]:
""" """

View file

@ -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(

View file

@ -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

View file

@ -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,
} }

View file

@ -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

View file

@ -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"])
} }

View file

@ -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: