refactor: remove dead code — 1,784 lines across 77 files (#9180)
Deep scan with vulture, pyflakes, and manual cross-referencing identified: - 41 dead functions/methods (zero callers in production) - 7 production-dead functions (only test callers, tests deleted) - 5 dead constants/variables - ~35 unused imports across agent/, hermes_cli/, tools/, gateway/ Categories of dead code removed: - Refactoring leftovers: _set_default_model, _setup_copilot_reasoning_selection, rebuild_lookups, clear_session_context, get_logs_dir, clear_session - Unused API surface: search_models_dev, get_pricing, skills_categories, get_read_files_summary, clear_read_tracker, menu_labels, get_spinner_list - Dead compatibility wrappers: schedule_cronjob, list_cronjobs, remove_cronjob - Stale debug helpers: get_debug_session_info copies in 4 tool files (centralized version in debug_helpers.py already exists) - Dead gateway methods: send_emote, send_notice (matrix), send_reaction (bluebubbles), _normalize_inbound_text (feishu), fetch_room_history (matrix), _start_typing_indicator (signal), parse_feishu_post_content - Dead constants: NOUS_API_BASE_URL, SKILLS_TOOL_DESCRIPTION, FILE_TOOLS, VALID_ASPECT_RATIOS, MEMORY_DIR - Unused UI code: _interactive_provider_selection, _interactive_model_selection (superseded by prompt_toolkit picker) Test suite verified: 609 tests covering affected files all pass. Tests for removed functions deleted. Tests using removed utilities (clear_read_tracker, MEMORY_DIR) updated to use internal APIs directly.
This commit is contained in:
@@ -352,19 +352,6 @@ def load_permanent(patterns: set):
|
||||
_permanent_approved.update(patterns)
|
||||
|
||||
|
||||
def clear_session(session_key: str):
|
||||
"""Clear all approvals and pending requests for a session."""
|
||||
with _lock:
|
||||
_session_approved.pop(session_key, None)
|
||||
_session_yolo.discard(session_key)
|
||||
_pending.pop(session_key, None)
|
||||
_gateway_notify_cbs.pop(session_key, None)
|
||||
# Signal ALL blocked threads so they don't hang forever
|
||||
entries = _gateway_queues.pop(session_key, [])
|
||||
for entry in entries:
|
||||
entry.event.set()
|
||||
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Config persistence for permanent allowlist
|
||||
|
||||
@@ -382,42 +382,6 @@ def cronjob(
|
||||
return tool_error(str(e), success=False)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Compatibility wrappers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def schedule_cronjob(
|
||||
prompt: str,
|
||||
schedule: str,
|
||||
name: Optional[str] = None,
|
||||
repeat: Optional[int] = None,
|
||||
deliver: Optional[str] = None,
|
||||
model: Optional[str] = None,
|
||||
provider: Optional[str] = None,
|
||||
base_url: Optional[str] = None,
|
||||
task_id: str = None,
|
||||
) -> str:
|
||||
return cronjob(
|
||||
action="create",
|
||||
prompt=prompt,
|
||||
schedule=schedule,
|
||||
name=name,
|
||||
repeat=repeat,
|
||||
deliver=deliver,
|
||||
model=model,
|
||||
provider=provider,
|
||||
base_url=base_url,
|
||||
task_id=task_id,
|
||||
)
|
||||
|
||||
|
||||
def list_cronjobs(include_disabled: bool = False, task_id: str = None) -> str:
|
||||
return cronjob(action="list", include_disabled=include_disabled, task_id=task_id)
|
||||
|
||||
|
||||
def remove_cronjob(job_id: str, task_id: str = None) -> str:
|
||||
return cronjob(action="remove", job_id=job_id, task_id=task_id)
|
||||
|
||||
|
||||
CRONJOB_SCHEMA = {
|
||||
"name": "cronjob",
|
||||
|
||||
@@ -20,9 +20,7 @@ Both ``code_execution_tool.py`` and ``tools/environments/local.py`` consult
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from contextvars import ContextVar
|
||||
from pathlib import Path
|
||||
from typing import Iterable
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -449,38 +449,6 @@ def read_file_tool(path: str, offset: int = 1, limit: int = 500, task_id: str =
|
||||
return tool_error(str(e))
|
||||
|
||||
|
||||
def get_read_files_summary(task_id: str = "default") -> list:
|
||||
"""Return a list of files read in this session for the given task.
|
||||
|
||||
Used by context compression to preserve file-read history across
|
||||
compression boundaries.
|
||||
"""
|
||||
with _read_tracker_lock:
|
||||
task_data = _read_tracker.get(task_id, {})
|
||||
read_history = task_data.get("read_history", set())
|
||||
seen_paths: dict = {}
|
||||
for (path, offset, limit) in read_history:
|
||||
if path not in seen_paths:
|
||||
seen_paths[path] = []
|
||||
seen_paths[path].append(f"lines {offset}-{offset + limit - 1}")
|
||||
return [
|
||||
{"path": p, "regions": regions}
|
||||
for p, regions in sorted(seen_paths.items())
|
||||
]
|
||||
|
||||
|
||||
def clear_read_tracker(task_id: str = None):
|
||||
"""Clear the read tracker.
|
||||
|
||||
Call with a task_id to clear just that task, or without to clear all.
|
||||
Should be called when a session is destroyed to prevent memory leaks
|
||||
in long-running gateway processes.
|
||||
"""
|
||||
with _read_tracker_lock:
|
||||
if task_id:
|
||||
_read_tracker.pop(task_id, None)
|
||||
else:
|
||||
_read_tracker.clear()
|
||||
|
||||
|
||||
def reset_file_dedup(task_id: str = None):
|
||||
@@ -719,12 +687,6 @@ def search_tool(pattern: str, target: str = "content", path: str = ".",
|
||||
return tool_error(str(e))
|
||||
|
||||
|
||||
FILE_TOOLS = [
|
||||
{"name": "read_file", "function": read_file_tool},
|
||||
{"name": "write_file", "function": write_file_tool},
|
||||
{"name": "patch", "function": patch_tool},
|
||||
{"name": "search_files", "function": search_tool}
|
||||
]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -61,7 +61,6 @@ ASPECT_RATIO_MAP = {
|
||||
"square": "square_hd",
|
||||
"portrait": "portrait_16_9"
|
||||
}
|
||||
VALID_ASPECT_RATIOS = list(ASPECT_RATIO_MAP.keys())
|
||||
|
||||
# Configuration for automatic upscaling
|
||||
UPSCALER_MODEL = "fal-ai/clarity-upscaler"
|
||||
@@ -564,15 +563,6 @@ def check_image_generation_requirements() -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def get_debug_session_info() -> Dict[str, Any]:
|
||||
"""
|
||||
Get information about the current debug session.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: Dictionary containing debug session information
|
||||
"""
|
||||
return _debug.get_session_info()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
|
||||
@@ -44,11 +44,6 @@ def get_memory_dir() -> Path:
|
||||
"""Return the profile-scoped memories directory."""
|
||||
return get_hermes_home() / "memories"
|
||||
|
||||
# Backward-compatible alias — gateway/run.py imports this at runtime inside
|
||||
# a function body, so it gets the correct snapshot for that process. New code
|
||||
# should prefer get_memory_dir().
|
||||
MEMORY_DIR = get_memory_dir()
|
||||
|
||||
ENTRY_DELIMITER = "\n§\n"
|
||||
|
||||
|
||||
|
||||
@@ -416,29 +416,6 @@ def check_moa_requirements() -> bool:
|
||||
return check_openrouter_api_key()
|
||||
|
||||
|
||||
def get_debug_session_info() -> Dict[str, Any]:
|
||||
"""
|
||||
Get information about the current debug session.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: Dictionary containing debug session information
|
||||
"""
|
||||
return _debug.get_session_info()
|
||||
|
||||
|
||||
def get_available_models() -> Dict[str, List[str]]:
|
||||
"""
|
||||
Get information about available models for MoA processing.
|
||||
|
||||
Returns:
|
||||
Dict[str, List[str]]: Dictionary with reference and aggregator models
|
||||
"""
|
||||
return {
|
||||
"reference_models": REFERENCE_MODELS,
|
||||
"aggregator_models": [AGGREGATOR_MODEL],
|
||||
"supported_models": REFERENCE_MODELS + [AGGREGATOR_MODEL]
|
||||
}
|
||||
|
||||
|
||||
def get_moa_configuration() -> Dict[str, Any]:
|
||||
"""
|
||||
|
||||
@@ -872,55 +872,6 @@ def _unicode_char_name(char: str) -> str:
|
||||
return names.get(char, f"U+{ord(char):04X}")
|
||||
|
||||
|
||||
def _parse_llm_response(text: str, skill_name: str) -> List[Finding]:
|
||||
"""Parse the LLM's JSON response into Finding objects."""
|
||||
import json as json_mod
|
||||
|
||||
# Extract JSON from the response (handle markdown code blocks)
|
||||
text = text.strip()
|
||||
if text.startswith("```"):
|
||||
lines = text.split("\n")
|
||||
text = "\n".join(lines[1:-1] if lines[-1].startswith("```") else lines[1:])
|
||||
|
||||
try:
|
||||
data = json_mod.loads(text)
|
||||
except json_mod.JSONDecodeError:
|
||||
return []
|
||||
|
||||
if not isinstance(data, dict):
|
||||
return []
|
||||
|
||||
findings = []
|
||||
for item in data.get("findings", []):
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
desc = item.get("description", "")
|
||||
severity = item.get("severity", "medium")
|
||||
if severity not in ("critical", "high", "medium", "low"):
|
||||
severity = "medium"
|
||||
if desc:
|
||||
findings.append(Finding(
|
||||
pattern_id="llm_audit",
|
||||
severity=severity,
|
||||
category="llm-detected",
|
||||
file="(LLM analysis)",
|
||||
line=0,
|
||||
match=desc[:120],
|
||||
description=f"LLM audit: {desc}",
|
||||
))
|
||||
|
||||
return findings
|
||||
|
||||
|
||||
def _get_configured_model() -> str:
|
||||
"""Load the user's configured model from ~/.hermes/config.yaml."""
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
config = load_config()
|
||||
return config.get("model", "")
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Internal helpers
|
||||
|
||||
@@ -447,10 +447,6 @@ def _get_category_from_path(skill_path: Path) -> Optional[str]:
|
||||
return None
|
||||
|
||||
|
||||
# Token estimation — use the shared implementation from model_metadata.
|
||||
from agent.model_metadata import estimate_tokens_rough as _estimate_tokens
|
||||
|
||||
|
||||
def _parse_tags(tags_value) -> List[str]:
|
||||
"""
|
||||
Parse tags from frontmatter value.
|
||||
@@ -629,85 +625,6 @@ def _load_category_description(category_dir: Path) -> Optional[str]:
|
||||
return None
|
||||
|
||||
|
||||
def skills_categories(verbose: bool = False, task_id: str = None) -> str:
|
||||
"""
|
||||
List available skill categories with descriptions (progressive disclosure tier 0).
|
||||
|
||||
Returns category names and descriptions for efficient discovery before drilling down.
|
||||
Categories can have a DESCRIPTION.md file with a description frontmatter field
|
||||
or first paragraph to explain what skills are in that category.
|
||||
|
||||
Args:
|
||||
verbose: If True, include skill counts per category (default: False, but currently always included)
|
||||
task_id: Optional task identifier used to probe the active backend
|
||||
|
||||
Returns:
|
||||
JSON string with list of categories and their descriptions
|
||||
"""
|
||||
try:
|
||||
# Use module-level SKILLS_DIR (respects monkeypatching) + external dirs
|
||||
all_dirs = [SKILLS_DIR] if SKILLS_DIR.exists() else []
|
||||
try:
|
||||
from agent.skill_utils import get_external_skills_dirs
|
||||
all_dirs.extend(d for d in get_external_skills_dirs() if d.exists())
|
||||
except Exception:
|
||||
pass
|
||||
if not all_dirs:
|
||||
return json.dumps(
|
||||
{
|
||||
"success": True,
|
||||
"categories": [],
|
||||
"message": "No skills directory found.",
|
||||
},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
category_dirs = {}
|
||||
category_counts: Dict[str, int] = {}
|
||||
for scan_dir in all_dirs:
|
||||
for skill_md in scan_dir.rglob("SKILL.md"):
|
||||
if any(part in _EXCLUDED_SKILL_DIRS for part in skill_md.parts):
|
||||
continue
|
||||
|
||||
try:
|
||||
frontmatter, _ = _parse_frontmatter(
|
||||
skill_md.read_text(encoding="utf-8")[:4000]
|
||||
)
|
||||
except Exception:
|
||||
frontmatter = {}
|
||||
|
||||
if not skill_matches_platform(frontmatter):
|
||||
continue
|
||||
|
||||
category = _get_category_from_path(skill_md)
|
||||
if category:
|
||||
category_counts[category] = category_counts.get(category, 0) + 1
|
||||
if category not in category_dirs:
|
||||
category_dirs[category] = skill_md.parent.parent
|
||||
|
||||
categories = []
|
||||
for name in sorted(category_dirs.keys()):
|
||||
category_dir = category_dirs[name]
|
||||
description = _load_category_description(category_dir)
|
||||
|
||||
cat_entry = {"name": name, "skill_count": category_counts[name]}
|
||||
if description:
|
||||
cat_entry["description"] = description
|
||||
categories.append(cat_entry)
|
||||
|
||||
return json.dumps(
|
||||
{
|
||||
"success": True,
|
||||
"categories": categories,
|
||||
"hint": "If a category is relevant to your task, use skills_list with that category to see available skills",
|
||||
},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return tool_error(str(e), success=False)
|
||||
|
||||
|
||||
def skills_list(category: str = None, task_id: str = None) -> str:
|
||||
"""
|
||||
List all available skills (progressive disclosure tier 1 - minimal metadata).
|
||||
@@ -1240,19 +1157,6 @@ def skill_view(name: str, file_path: str = None, task_id: str = None) -> str:
|
||||
return tool_error(str(e), success=False)
|
||||
|
||||
|
||||
# Tool description for model_tools.py
|
||||
SKILLS_TOOL_DESCRIPTION = """Access skill documents providing specialized instructions, guidelines, and executable knowledge.
|
||||
|
||||
Progressive disclosure workflow:
|
||||
1. skills_list() - Returns metadata (name, description, tags, linked_file_count) for all skills
|
||||
2. skill_view(name) - Loads full SKILL.md content + shows available linked_files
|
||||
3. skill_view(name, file_path) - Loads specific linked file (e.g., 'references/api.md', 'scripts/train.py')
|
||||
|
||||
Skills may include:
|
||||
- references/: Additional documentation, API specs, examples
|
||||
- templates/: Output formats, config files, boilerplate code
|
||||
- assets/: Supplementary files (agentskills.io standard)
|
||||
- scripts/: Executable helpers (Python, shell scripts)"""
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -56,9 +56,6 @@ from tools.interrupt import is_interrupted, _interrupt_event # noqa: F401 — r
|
||||
# display_hermes_home imported lazily at call site (stale-module safety during hermes update)
|
||||
|
||||
|
||||
def ensure_minisweagent_on_path(_repo_root: Path | None = None) -> None:
|
||||
"""Backward-compatible no-op after minisweagent_path.py removal."""
|
||||
return
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -140,7 +137,6 @@ def set_approval_callback(cb):
|
||||
|
||||
# Dangerous command detection + approval now consolidated in tools/approval.py
|
||||
from tools.approval import (
|
||||
check_dangerous_command as _check_dangerous_command_impl,
|
||||
check_all_command_guards as _check_all_guards_impl,
|
||||
)
|
||||
|
||||
@@ -937,29 +933,6 @@ def is_persistent_env(task_id: str) -> bool:
|
||||
return bool(getattr(env, "_persistent", False))
|
||||
|
||||
|
||||
def get_active_environments_info() -> Dict[str, Any]:
|
||||
"""Get information about currently active environments."""
|
||||
info = {
|
||||
"count": len(_active_environments),
|
||||
"task_ids": list(_active_environments.keys()),
|
||||
"workdirs": {},
|
||||
}
|
||||
|
||||
# Calculate total disk usage (per-task to avoid double-counting)
|
||||
total_size = 0
|
||||
for task_id in _active_environments:
|
||||
scratch_dir = _get_scratch_dir()
|
||||
pattern = f"hermes-*{task_id[:8]}*"
|
||||
import glob
|
||||
for path in glob.glob(str(scratch_dir / pattern)):
|
||||
try:
|
||||
size = sum(f.stat().st_size for f in Path(path).rglob('*') if f.is_file())
|
||||
total_size += size
|
||||
except OSError as e:
|
||||
logger.debug("Could not stat path %s: %s", path, e)
|
||||
|
||||
info["total_disk_usage_mb"] = round(total_size / (1024 * 1024), 2)
|
||||
return info
|
||||
|
||||
|
||||
def cleanup_all_environments():
|
||||
|
||||
@@ -37,8 +37,6 @@ from utils import is_truthy_value
|
||||
from tools.managed_tool_gateway import resolve_managed_tool_gateway
|
||||
from tools.tool_backend_helpers import managed_nous_tools_enabled, resolve_openai_audio_api_key
|
||||
|
||||
from hermes_constants import get_hermes_home
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -93,35 +91,6 @@ _local_model_name: Optional[str] = None
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def get_stt_model_from_config() -> Optional[str]:
|
||||
"""Read the STT model name from ~/.hermes/config.yaml.
|
||||
|
||||
Provider-aware: reads from the correct provider-specific section
|
||||
(``stt.local.model``, ``stt.openai.model``, etc.). Falls back to
|
||||
the legacy flat ``stt.model`` key only for cloud providers — if the
|
||||
resolved provider is ``local`` the legacy key is ignored to prevent
|
||||
OpenAI model names (e.g. ``whisper-1``) from being fed to
|
||||
faster-whisper.
|
||||
|
||||
Silently returns ``None`` on any error (missing file, bad YAML, etc.).
|
||||
"""
|
||||
try:
|
||||
stt_cfg = _load_stt_config()
|
||||
provider = stt_cfg.get("provider", DEFAULT_PROVIDER)
|
||||
# Read from the provider-specific section first
|
||||
provider_model = stt_cfg.get(provider, {}).get("model")
|
||||
if provider_model:
|
||||
return provider_model
|
||||
# Legacy flat key — only honour for non-local providers to avoid
|
||||
# feeding OpenAI model names (whisper-1) to faster-whisper.
|
||||
if provider not in ("local", "local_command"):
|
||||
legacy = stt_cfg.get("model")
|
||||
if legacy:
|
||||
return legacy
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def _load_stt_config() -> dict:
|
||||
"""Load the ``stt`` section from user config, falling back to defaults."""
|
||||
|
||||
@@ -689,15 +689,6 @@ def check_vision_requirements() -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def get_debug_session_info() -> Dict[str, Any]:
|
||||
"""
|
||||
Get information about the current debug session.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: Dictionary containing debug session information
|
||||
"""
|
||||
return _debug.get_session_info()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
|
||||
@@ -63,11 +63,6 @@ def _termux_microphone_command() -> Optional[str]:
|
||||
return shutil.which("termux-microphone-record")
|
||||
|
||||
|
||||
def _termux_media_player_command() -> Optional[str]:
|
||||
if not _is_termux_environment():
|
||||
return None
|
||||
return shutil.which("termux-media-player")
|
||||
|
||||
|
||||
def _termux_api_app_installed() -> bool:
|
||||
if not _is_termux_environment():
|
||||
|
||||
@@ -1932,9 +1932,6 @@ def check_auxiliary_model() -> bool:
|
||||
return client is not None
|
||||
|
||||
|
||||
def get_debug_session_info() -> Dict[str, Any]:
|
||||
"""Get information about the current debug session."""
|
||||
return _debug.get_session_info()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user