refactor(restructure): rewrite all imports for hermes_agent package

Rewrite all import statements, patch() targets, sys.modules keys,
importlib.import_module() strings, and subprocess -m references to use
hermes_agent.* paths.

Strip sys.path.insert hacks from production code (rely on editable install).
Update COMPONENT_PREFIXES for logger filtering.
Fix 3 hardcoded getLogger() calls to use __name__.
Update transport and tool registry discovery paths.
Update plugin module path strings.
Add legacy process-name patterns for gateway PID detection.
Add main() to skills_sync for console_script entry point.
Fix _get_bundled_dir() path traversal after move.

Part of #14182, #14183
This commit is contained in:
alt-glitch 2026-04-23 08:35:34 +05:30
parent 65ca3ba93b
commit 4b16341975
898 changed files with 12494 additions and 12019 deletions

View file

@ -21,11 +21,11 @@ from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, Set, TYPE_CHECKING
if TYPE_CHECKING:
from tools.budget_config import BudgetConfig
from hermes_agent.tools.budget_config import BudgetConfig
from model_tools import handle_function_call
from tools.terminal_tool import get_active_env
from tools.tool_result_storage import maybe_persist_tool_result, enforce_turn_budget
from hermes_agent.tools.dispatch import handle_function_call
from hermes_agent.tools.terminal import get_active_env
from hermes_agent.tools.result_storage import maybe_persist_tool_result, enforce_turn_budget
# Thread pool for running sync tool calls that internally use asyncio.run()
# (e.g., the Modal/Docker/Daytona terminal backends). Running them in a separate
@ -164,7 +164,7 @@ class HermesAgentLoop:
thresholds, per-turn aggregate budget, and preview size.
If None, uses DEFAULT_BUDGET (current hardcoded values).
"""
from tools.budget_config import DEFAULT_BUDGET
from hermes_agent.tools.budget_config import DEFAULT_BUDGET
self.server = server
self.tool_schemas = tool_schemas
self.valid_tool_names = valid_tool_names
@ -190,7 +190,7 @@ class HermesAgentLoop:
tool_errors: List[ToolError] = []
# Per-loop TodoStore for the todo tool (ephemeral, dies with the loop)
from tools.todo_tool import TodoStore, todo_tool as _todo_tool
from hermes_agent.tools.todo import TodoStore, todo_tool as _todo_tool
_todo_store = TodoStore()
# Extract user task from first user message for browser_snapshot context

View file

@ -60,7 +60,7 @@ from atroposlib.envs.server_handling.server_manager import APIServerConfig
from environments.agent_loop import AgentResult, HermesAgentLoop
from environments.hermes_base_env import HermesAgentBaseEnv, HermesAgentEnvConfig
from environments.tool_context import ToolContext
from tools.terminal_tool import (
from hermes_agent.tools.terminal import (
register_task_env_overrides,
clear_task_env_overrides,
cleanup_vm,
@ -876,7 +876,7 @@ class TerminalBench2EvalEnv(HermesAgentBaseEnv):
# Let cancellations propagate (finally blocks run cleanup_vm)
await asyncio.gather(*eval_tasks, return_exceptions=True)
# Belt-and-suspenders: clean up any remaining sandboxes
from tools.terminal_tool import cleanup_all_environments
from hermes_agent.tools.terminal import cleanup_all_environments
cleanup_all_environments()
print("All sandboxes cleaned up.")
return
@ -984,7 +984,7 @@ class TerminalBench2EvalEnv(HermesAgentBaseEnv):
# Kill all remaining sandboxes. Timed-out tasks leave orphaned thread
# pool workers still executing commands -- cleanup_all stops them.
from tools.terminal_tool import cleanup_all_environments
from hermes_agent.tools.terminal import cleanup_all_environments
print("\nCleaning up all sandboxes...")
cleanup_all_environments()

View file

@ -709,7 +709,7 @@ class YCBenchEvalEnv(HermesAgentBaseEnv):
tqdm.write("\n[INTERRUPTED] Stopping evaluation...")
pbar.close()
try:
from tools.terminal_tool import cleanup_all_environments
from hermes_agent.tools.terminal import cleanup_all_environments
cleanup_all_environments()
except Exception:
pass
@ -819,7 +819,7 @@ class YCBenchEvalEnv(HermesAgentBaseEnv):
print(f"Results saved to: {self._streaming_path}")
try:
from tools.terminal_tool import cleanup_all_environments
from hermes_agent.tools.terminal import cleanup_all_environments
cleanup_all_environments()
except Exception:
pass

View file

@ -62,15 +62,15 @@ from atroposlib.type_definitions import Item
from environments.agent_loop import AgentResult, HermesAgentLoop
from environments.tool_context import ToolContext
from tools.budget_config import (
from hermes_agent.tools.budget_config import (
DEFAULT_RESULT_SIZE_CHARS,
DEFAULT_TURN_BUDGET_CHARS,
DEFAULT_PREVIEW_SIZE_CHARS,
)
# Import hermes-agent toolset infrastructure
from model_tools import get_tool_definitions
from toolset_distributions import sample_toolsets_from_distribution
from hermes_agent.tools.dispatch import get_tool_definitions
from hermes_agent.tools.distributions import sample_toolsets_from_distribution
logger = logging.getLogger(__name__)
@ -209,7 +209,7 @@ class HermesAgentEnvConfig(BaseEnvConfig):
def build_budget_config(self):
"""Build a BudgetConfig from env config fields."""
from tools.budget_config import BudgetConfig
from hermes_agent.tools.budget_config import BudgetConfig
return BudgetConfig(
default_result_size=self.default_result_size_chars,
turn_budget=self.turn_budget_chars,

View file

@ -31,9 +31,9 @@ from typing import Any, Dict, List, Optional
import asyncio
import concurrent.futures
from model_tools import handle_function_call
from tools.terminal_tool import cleanup_vm
from tools.browser_tool import cleanup_browser
from hermes_agent.tools.dispatch import handle_function_call
from hermes_agent.tools.terminal import cleanup_vm
from hermes_agent.tools.browser.tool import cleanup_browser
logger = logging.getLogger(__name__)
@ -446,7 +446,7 @@ class ToolContext:
"""
# Kill any background processes from this rollout (safety net)
try:
from tools.process_registry import process_registry
from hermes_agent.tools.process_registry import process_registry
killed = process_registry.kill_all(task_id=self.task_id)
if killed:
logger.debug("Process cleanup for task %s: killed %d process(es)", self.task_id, killed)

2
hermes
View file

@ -7,5 +7,5 @@ subcommands such as `gateway`, `cron`, and `doctor`.
"""
if __name__ == "__main__":
from hermes_cli.main import main
from hermes_agent.cli.main import main
main()

View file

@ -8,7 +8,7 @@ from typing import Optional
def detect_provider() -> Optional[str]:
"""Resolve the active Hermes runtime provider, or None if unavailable."""
try:
from hermes_cli.runtime_provider import resolve_runtime_provider
from hermes_agent.cli.runtime_provider import resolve_runtime_provider
runtime = resolve_runtime_provider()
api_key = runtime.get("api_key")
provider = runtime.get("provider")

View file

@ -17,7 +17,7 @@ import asyncio
import logging
import sys
from pathlib import Path
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
# Methods clients send as periodic liveness probes. They are not part of the
@ -83,7 +83,7 @@ def _setup_logging() -> None:
def _load_env() -> None:
"""Load .env from HERMES_HOME (default ``~/.hermes``)."""
from hermes_cli.env_loader import load_hermes_dotenv
from hermes_agent.cli.env_loader import load_hermes_dotenv
hermes_home = get_hermes_home()
loaded = load_hermes_dotenv(hermes_home=hermes_home)
@ -104,11 +104,6 @@ def main() -> None:
logger = logging.getLogger(__name__)
logger.info("Starting hermes-agent ACP adapter")
# Ensure the project root is on sys.path so ``from run_agent import AIAgent`` works
project_root = str(Path(__file__).resolve().parent.parent)
if project_root not in sys.path:
sys.path.insert(0, project_root)
import acp
from .server import HermesACPAgent

View file

@ -88,7 +88,7 @@ def make_tool_progress_cb(
snapshot = None
if name in {"write_file", "patch", "skill_manage"}:
try:
from agent.display import capture_local_edit_snapshot
from hermes_agent.agent.display import capture_local_edit_snapshot
snapshot = capture_local_edit_snapshot(name, args)
except Exception:

View file

@ -52,20 +52,20 @@ try:
except ImportError:
from acp.schema import AuthMethod as AuthMethodAgent # type: ignore[attr-defined]
from acp_adapter.auth import detect_provider
from acp_adapter.events import (
from hermes_agent.acp.auth import detect_provider
from hermes_agent.acp.events import (
make_message_cb,
make_step_cb,
make_thinking_cb,
make_tool_progress_cb,
)
from acp_adapter.permissions import make_approval_callback
from acp_adapter.session import SessionManager, SessionState
from hermes_agent.acp.permissions import make_approval_callback
from hermes_agent.acp.session import SessionManager, SessionState
logger = logging.getLogger(__name__)
try:
from hermes_cli import __version__ as HERMES_VERSION
from hermes_agent.cli import __version__ as HERMES_VERSION
except Exception:
HERMES_VERSION = "0.0.0"
@ -172,7 +172,7 @@ class HermesACPAgent(acp.Agent):
provider = getattr(state.agent, "provider", None) or detect_provider() or "openrouter"
try:
from hermes_cli.models import curated_models_for_provider, normalize_provider, provider_label
from hermes_agent.cli.models.models import curated_models_for_provider, normalize_provider, provider_label
normalized_provider = normalize_provider(provider)
provider_name = provider_label(normalized_provider)
@ -235,7 +235,7 @@ class HermesACPAgent(acp.Agent):
new_model = raw_model.strip()
try:
from hermes_cli.models import detect_provider_for_model, parse_model_input
from hermes_agent.cli.models.models import detect_provider_for_model, parse_model_input
target_provider, new_model = parse_model_input(new_model, current_provider)
if target_provider == current_provider:
@ -257,7 +257,7 @@ class HermesACPAgent(acp.Agent):
return
try:
from tools.mcp_tool import register_mcp_servers
from hermes_agent.tools.mcp.tool import register_mcp_servers
config_map: dict[str, dict] = {}
for server in mcp_servers:
@ -285,7 +285,7 @@ class HermesACPAgent(acp.Agent):
return
try:
from model_tools import get_tool_definitions
from hermes_agent.tools.dispatch import get_tool_definitions
enabled_toolsets = getattr(state.agent, "enabled_toolsets", None) or ["hermes-acp"]
disabled_toolsets = getattr(state.agent, "disabled_toolsets", None)
@ -572,7 +572,7 @@ class HermesACPAgent(acp.Agent):
nonlocal previous_approval_cb, previous_interactive
if approval_cb:
try:
from tools import terminal_tool as _terminal_tool
from hermes_agent.tools import terminal_tool as _terminal_tool
previous_approval_cb = _terminal_tool._get_approval_callback()
_terminal_tool.set_approval_callback(approval_cb)
except Exception:
@ -599,7 +599,7 @@ class HermesACPAgent(acp.Agent):
os.environ["HERMES_INTERACTIVE"] = previous_interactive
if approval_cb:
try:
from tools import terminal_tool as _terminal_tool
from hermes_agent.tools import terminal_tool as _terminal_tool
_terminal_tool.set_approval_callback(previous_approval_cb)
except Exception:
logger.debug("Could not restore approval callback", exc_info=True)
@ -618,7 +618,7 @@ class HermesACPAgent(acp.Agent):
final_response = result.get("final_response", "")
if final_response:
try:
from agent.title_generator import maybe_auto_title
from hermes_agent.agent.title_generator import maybe_auto_title
maybe_auto_title(
self.session_manager._get_db(),
@ -753,7 +753,7 @@ class HermesACPAgent(acp.Agent):
def _cmd_tools(self, args: str, state: SessionState) -> str:
try:
from model_tools import get_tool_definitions
from hermes_agent.tools.dispatch import get_tool_definitions
toolsets = getattr(state.agent, "enabled_toolsets", None) or ["hermes-acp"]
tools = get_tool_definitions(enabled_toolsets=toolsets, quiet_mode=True)
if not tools:
@ -804,7 +804,7 @@ class HermesACPAgent(acp.Agent):
if not hasattr(agent, "_compress_context"):
return "Context compression not available for this agent."
from agent.model_metadata import estimate_messages_tokens_rough
from hermes_agent.providers.metadata import estimate_messages_tokens_rough
original_count = len(state.history)
approx_tokens = estimate_messages_tokens_rough(state.history)

View file

@ -8,7 +8,7 @@ history.
"""
from __future__ import annotations
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
import copy
import json
@ -100,7 +100,7 @@ def _register_task_cwd(task_id: str, cwd: str) -> None:
if not task_id:
return
try:
from tools.terminal_tool import register_task_env_overrides
from hermes_agent.tools.terminal import register_task_env_overrides
register_task_env_overrides(task_id, {"cwd": cwd})
except Exception:
logger.debug("Failed to register ACP task cwd override", exc_info=True)
@ -111,7 +111,7 @@ def _clear_task_cwd(task_id: str) -> None:
if not task_id:
return
try:
from tools.terminal_tool import clear_task_env_overrides
from hermes_agent.tools.terminal import clear_task_env_overrides
clear_task_env_overrides(task_id)
except Exception:
logger.debug("Failed to clear ACP task cwd override", exc_info=True)
@ -355,7 +355,7 @@ class SessionManager:
if self._db_instance is not None:
return self._db_instance
try:
from hermes_state import SessionDB
from hermes_agent.state import SessionDB
hermes_home = get_hermes_home()
self._db_instance = SessionDB(db_path=hermes_home / "state.db")
return self._db_instance
@ -523,9 +523,9 @@ class SessionManager:
if self._agent_factory is not None:
return self._agent_factory()
from run_agent import AIAgent
from hermes_cli.config import load_config
from hermes_cli.runtime_provider import resolve_runtime_provider
from hermes_agent.agent.loop import AIAgent
from hermes_agent.cli.config import load_config
from hermes_agent.cli.runtime_provider import resolve_runtime_provider
config = load_config()
model_cfg = config.get("model")

View file

@ -103,7 +103,7 @@ def _build_patch_mode_content(patch_text: str) -> List[Any]:
return [acp.tool_content(acp.text_block(""))]
try:
from tools.patch_parser import OperationType, parse_v4a_patch
from hermes_agent.tools.patch_parser import OperationType, parse_v4a_patch
operations, error = parse_v4a_patch(patch_text)
if error or not operations:
@ -243,7 +243,7 @@ def _build_tool_complete_content(
if tool_name in {"write_file", "patch", "skill_manage"}:
try:
from agent.display import extract_edit_diff
from hermes_agent.agent.display import extract_edit_diff
diff_text = extract_edit_diff(
tool_name,

View file

@ -24,14 +24,14 @@ import re
import time
from typing import Any, Dict, List, Optional
from agent.auxiliary_client import call_llm
from agent.context_engine import ContextEngine
from agent.model_metadata import (
from hermes_agent.providers.auxiliary import call_llm
from hermes_agent.agent.context.engine import ContextEngine
from hermes_agent.providers.metadata import (
MINIMUM_CONTEXT_LENGTH,
get_model_context_length,
estimate_messages_tokens_rough,
)
from agent.redact import redact_sensitive_text
from hermes_agent.agent.redact import redact_sensitive_text
logger = logging.getLogger(__name__)

View file

@ -11,7 +11,7 @@ from dataclasses import dataclass, field
from pathlib import Path
from typing import Awaitable, Callable
from agent.model_metadata import estimate_tokens_rough
from hermes_agent.providers.metadata import estimate_tokens_rough
_QUOTED_REFERENCE_VALUE = r'(?:`[^`\n]+`|"[^"\n]+"|\'[^\'\n]+\')'
REFERENCE_PATTERN = re.compile(
@ -315,7 +315,7 @@ async def _fetch_url_content(
async def _default_url_fetcher(url: str) -> str:
from tools.web_tools import web_extract_tool
from hermes_agent.tools.web import web_extract_tool
raw = await web_extract_tool([url], format="markdown", use_llm_processing=True)
payload = json.loads(raw)
@ -340,7 +340,7 @@ def _resolve_path(cwd: Path, target: str, *, allowed_root: Path | None = None) -
def _ensure_reference_path_allowed(path: Path) -> None:
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
home = Path(os.path.expanduser("~")).resolve()
hermes_home = get_hermes_home().resolve()

View file

@ -21,8 +21,8 @@ from pathlib import Path
from types import SimpleNamespace
from typing import Any
from agent.file_safety import get_read_block_error, is_write_denied
from agent.redact import redact_sensitive_text
from hermes_agent.agent.file_safety import get_read_block_error, is_write_denied
from hermes_agent.agent.redact import redact_sensitive_text
ACP_MARKER_BASE_URL = "acp://copilot"
_DEFAULT_TIMEOUT_SECONDS = 900.0

View file

@ -13,7 +13,7 @@ from dataclasses import dataclass, field
from difflib import unified_diff
from pathlib import Path
from utils import safe_json_loads
from hermes_agent.utils import safe_json_loads
# ANSI escape codes for coloring tool failure indicators
_RED = "\033[31m"
@ -43,7 +43,7 @@ def _diff_ansi() -> dict[str, str]:
plus = "\033[38;2;255;255;255;48;2;20;90;20m"
try:
from hermes_cli.skin_engine import get_active_skin
from hermes_agent.cli.ui.skin_engine import get_active_skin
skin = get_active_skin()
def _hex_fg(key: str, fallback_rgb: tuple[int, int, int]) -> str:
@ -118,7 +118,7 @@ def get_tool_preview_max_len() -> int:
def _get_skin():
"""Get the active skin config, or None if not available."""
try:
from hermes_cli.skin_engine import get_active_skin
from hermes_agent.cli.ui.skin_engine import get_active_skin
return get_active_skin()
except Exception:
return None
@ -148,7 +148,7 @@ def get_tool_emoji(tool_name: str, default: str = "⚡") -> str:
return override
# 2. Registry default
try:
from tools.registry import registry
from hermes_agent.tools.registry import registry
emoji = registry.get_emoji(tool_name, default="")
if emoji:
return emoji
@ -311,7 +311,7 @@ def _resolve_skill_manage_paths(args: dict) -> list[Path]:
if not action or not name:
return []
from tools.skill_manager_tool import _find_skill, _resolve_skill_dir
from hermes_agent.tools.skills.manager import _find_skill, _resolve_skill_dir
if action == "create":
skill_dir = _resolve_skill_dir(name, args.get("category"))

View file

@ -10,7 +10,7 @@ from typing import Optional
def _hermes_home_path() -> Path:
"""Resolve the active HERMES_HOME (profile-aware) without circular imports."""
try:
from hermes_constants import get_hermes_home # local import to avoid cycles
from hermes_agent.constants import get_hermes_home # local import to avoid cycles
return get_hermes_home()
except Exception:
return Path(os.path.expanduser("~/.hermes"))

View file

@ -164,7 +164,7 @@ def resolve_aspect_ratio(value: Optional[str]) -> str:
def _images_cache_dir() -> Path:
"""Return ``$HERMES_HOME/cache/images/``, creating parents as needed."""
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
path = get_hermes_home() / "cache" / "images"
path.mkdir(parents=True, exist_ok=True)

View file

@ -24,7 +24,7 @@ import logging
import threading
from typing import Dict, List, Optional
from agent.image_gen_provider import ImageGenProvider
from hermes_agent.agent.image_gen.provider import ImageGenProvider
logger = logging.getLogger(__name__)
@ -80,7 +80,7 @@ def get_active_provider() -> Optional[ImageGenProvider]:
"""
configured: Optional[str] = None
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
cfg = load_config()
section = cfg.get("image_gen") if isinstance(cfg, dict) else None

View file

@ -10,7 +10,7 @@ multi-platform architecture with additional cost estimation and platform
breakdown capabilities.
Usage:
from agent.insights import InsightsEngine
from hermes_agent.agent.insights import InsightsEngine
engine = InsightsEngine(db)
report = engine.generate(days=30)
print(engine.format_terminal(report))
@ -22,7 +22,7 @@ from collections import Counter, defaultdict
from datetime import datetime
from typing import Any, Dict, List
from agent.usage_pricing import (
from hermes_agent.providers.pricing import (
CanonicalUsage,
DEFAULT_PRICING,
estimate_usage_cost,

View file

@ -14,7 +14,7 @@ Features:
- Support for multiple model providers
Usage:
from run_agent import AIAgent
from hermes_agent.agent.loop import AIAgent
agent = AIAgent(base_url="http://localhost:30000/v1", model="claude-opus-4-20250514")
response = agent.run_conversation("Tell me about the latest Python updates")
@ -40,18 +40,18 @@ import uuid
from typing import Callable, List, Dict, Any, Optional, TYPE_CHECKING
if TYPE_CHECKING:
from agent.rate_limit_tracker import RateLimitState
from hermes_agent.providers.rate_limiting import RateLimitState
from openai import OpenAI
import fire
from datetime import datetime
from pathlib import Path
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
# Load .env from ~/.hermes/.env first, then project root as dev fallback.
# User-managed env files should override stale shell exports on restart.
from hermes_cli.env_loader import load_hermes_dotenv
from hermes_cli.timeouts import (
from hermes_agent.cli.env_loader import load_hermes_dotenv
from hermes_agent.cli.timeouts import (
get_provider_request_timeout,
get_provider_stale_timeout,
)
@ -67,30 +67,30 @@ else:
# Import our tool system
from model_tools import (
from hermes_agent.tools.dispatch import (
get_tool_definitions,
get_toolset_for_tool,
handle_function_call,
check_toolset_requirements,
)
from tools.terminal_tool import cleanup_vm, get_active_env, is_persistent_env
from tools.tool_result_storage import maybe_persist_tool_result, enforce_turn_budget
from tools.interrupt import set_interrupt as _set_interrupt
from tools.browser_tool import cleanup_browser
from hermes_agent.tools.terminal import cleanup_vm, get_active_env, is_persistent_env
from hermes_agent.tools.result_storage import maybe_persist_tool_result, enforce_turn_budget
from hermes_agent.tools.interrupt import set_interrupt as _set_interrupt
from hermes_agent.tools.browser.tool import cleanup_browser
from hermes_constants import OPENROUTER_BASE_URL
from hermes_agent.constants import OPENROUTER_BASE_URL
# Agent internals extracted to agent/ package for modularity
from agent.memory_manager import build_memory_context_block, sanitize_context
from agent.retry_utils import jittered_backoff
from agent.error_classifier import classify_api_error, FailoverReason
from agent.prompt_builder import (
from hermes_agent.agent.memory.manager import build_memory_context_block, sanitize_context
from hermes_agent.providers.retry import jittered_backoff
from hermes_agent.providers.errors import classify_api_error, FailoverReason
from hermes_agent.agent.prompt_builder import (
DEFAULT_AGENT_IDENTITY, PLATFORM_HINTS,
MEMORY_GUIDANCE, SESSION_SEARCH_GUIDANCE, SKILLS_GUIDANCE,
build_nous_subscription_prompt,
)
from agent.model_metadata import (
from hermes_agent.providers.metadata import (
fetch_model_metadata,
estimate_tokens_rough, estimate_messages_tokens_rough, estimate_request_tokens_rough,
get_next_probe_tier, parse_context_limit_from_error,
@ -98,12 +98,12 @@ from agent.model_metadata import (
save_context_length, is_local_endpoint,
query_ollama_num_ctx,
)
from agent.context_compressor import ContextCompressor
from agent.subdirectory_hints import SubdirectoryHintTracker
from agent.prompt_caching import apply_anthropic_cache_control
from agent.prompt_builder import build_skills_system_prompt, build_context_files_prompt, build_environment_hints, load_soul_md, TOOL_USE_ENFORCEMENT_GUIDANCE, TOOL_USE_ENFORCEMENT_MODELS, DEVELOPER_ROLE_MODELS, GOOGLE_MODEL_OPERATIONAL_GUIDANCE, OPENAI_MODEL_EXECUTION_GUIDANCE
from agent.usage_pricing import estimate_usage_cost, normalize_usage
from agent.codex_responses_adapter import (
from hermes_agent.agent.context.compressor import ContextCompressor
from hermes_agent.agent.subdirectory_hints import SubdirectoryHintTracker
from hermes_agent.providers.caching import apply_anthropic_cache_control
from hermes_agent.agent.prompt_builder import build_skills_system_prompt, build_context_files_prompt, build_environment_hints, load_soul_md, TOOL_USE_ENFORCEMENT_GUIDANCE, TOOL_USE_ENFORCEMENT_MODELS, DEVELOPER_ROLE_MODELS, GOOGLE_MODEL_OPERATIONAL_GUIDANCE, OPENAI_MODEL_EXECUTION_GUIDANCE
from hermes_agent.providers.pricing import estimate_usage_cost, normalize_usage
from hermes_agent.providers.codex_adapter import (
_chat_content_to_responses_parts,
_chat_messages_to_responses_input as _codex_chat_messages_to_responses_input,
_derive_responses_function_call_id as _codex_derive_responses_function_call_id,
@ -117,17 +117,17 @@ from agent.codex_responses_adapter import (
_split_responses_tool_id as _codex_split_responses_tool_id,
_summarize_user_message_for_log,
)
from agent.display import (
from hermes_agent.agent.display import (
KawaiiSpinner, build_tool_preview as _build_tool_preview,
get_cute_tool_message as _get_cute_tool_message_impl,
_detect_tool_failure,
get_tool_emoji as _get_tool_emoji,
)
from agent.trajectory import (
from hermes_agent.agent.trajectory import (
convert_scratchpad_to_think, has_incomplete_scratchpad,
save_trajectory as _save_trajectory_to_file,
)
from utils import atomic_json_write, base_url_host_matches, base_url_hostname, env_var_enabled, normalize_proxy_url
from hermes_agent.utils import atomic_json_write, base_url_host_matches, base_url_hostname, env_var_enabled, normalize_proxy_url
@ -876,7 +876,7 @@ class AIAgent:
self.api_mode = "chat_completions"
try:
from hermes_cli.model_normalize import (
from hermes_agent.cli.models.normalize import (
_AGGREGATOR_PROVIDERS,
normalize_model_for_provider,
)
@ -1026,7 +1026,7 @@ class AIAgent:
# Centralized logging — agent.log (INFO+) and errors.log (WARNING+)
# both live under ~/.hermes/logs/. Idempotent, so gateway mode
# (which creates a new AIAgent per message) won't duplicate handlers.
from hermes_logging import setup_logging, setup_verbose_logging
from hermes_agent.logging import setup_logging, setup_verbose_logging
setup_logging(hermes_home=_hermes_home)
if self.verbose_logging:
@ -1040,10 +1040,10 @@ class AIAgent:
# File handlers (agent.log, errors.log) still capture everything.
for quiet_logger in [
'tools', # all tools.* (terminal, browser, web, file, etc.)
'run_agent', # agent runner internals
'hermes_agent.agent.loop', # agent runner internals
'scripts.trajectory_compressor',
'cron', # scheduler (only relevant in daemon mode)
'hermes_cli', # CLI helpers
'hermes_agent.cli', # CLI helpers
]:
logging.getLogger(quiet_logger).setLevel(logging.ERROR)
@ -1085,12 +1085,12 @@ class AIAgent:
_provider_timeout = get_provider_request_timeout(self.provider, self.model)
if self.api_mode == "anthropic_messages":
from agent.anthropic_adapter import build_anthropic_client, resolve_anthropic_token
from hermes_agent.providers.anthropic_adapter import build_anthropic_client, resolve_anthropic_token
# Bedrock + Claude → use AnthropicBedrock SDK for full feature parity
# (prompt caching, thinking budgets, adaptive thinking).
_is_bedrock_anthropic = self.provider == "bedrock"
if _is_bedrock_anthropic:
from agent.anthropic_adapter import build_anthropic_bedrock_client
from hermes_agent.providers.anthropic_adapter import build_anthropic_bedrock_client
_region_match = re.search(r"bedrock-runtime\.([a-z0-9-]+)\.", base_url or "")
_br_region = _region_match.group(1) if _region_match else "us-east-1"
self._bedrock_region = _br_region
@ -1119,7 +1119,7 @@ class AIAgent:
# so injects Claude-Code identity headers and system prompts
# that cause 401/403 on their endpoints. Guards #1739 and
# the third-party identity-injection bug.
from agent.anthropic_adapter import _is_oauth_token as _is_oat
from hermes_agent.providers.anthropic_adapter import _is_oauth_token as _is_oat
self._is_anthropic_oauth = _is_oat(effective_key) if _is_native_anthropic else False
self._anthropic_client = build_anthropic_client(effective_key, base_url, timeout=_provider_timeout)
# No OpenAI client needed for Anthropic mode
@ -1137,7 +1137,7 @@ class AIAgent:
# Guardrail config — read from config.yaml at init time.
self._bedrock_guardrail_config = None
try:
from hermes_cli.config import load_config as _load_br_cfg
from hermes_agent.cli.config import load_config as _load_br_cfg
_gr = _load_br_cfg().get("bedrock", {}).get("guardrail", {})
if _gr.get("guardrail_identifier") and _gr.get("guardrail_version"):
self._bedrock_guardrail_config = {
@ -1173,7 +1173,7 @@ class AIAgent:
"X-OpenRouter-Categories": "productivity,cli-agent",
}
elif base_url_host_matches(effective_base, "api.githubcopilot.com"):
from hermes_cli.models import copilot_default_headers
from hermes_agent.cli.models.models import copilot_default_headers
client_kwargs["default_headers"] = copilot_default_headers()
elif base_url_host_matches(effective_base, "api.kimi.com"):
@ -1183,11 +1183,11 @@ class AIAgent:
elif base_url_host_matches(effective_base, "portal.qwen.ai"):
client_kwargs["default_headers"] = _qwen_portal_headers()
elif base_url_host_matches(effective_base, "chatgpt.com"):
from agent.auxiliary_client import _codex_cloudflare_headers
from hermes_agent.providers.auxiliary import _codex_cloudflare_headers
client_kwargs["default_headers"] = _codex_cloudflare_headers(api_key)
else:
# No explicit creds — use the centralized provider router
from agent.auxiliary_client import resolve_provider_client
from hermes_agent.providers.auxiliary import resolve_provider_client
_routed_client, _ = resolve_provider_client(
self.provider or "auto", model=self.model, raw_codex=True)
if _routed_client is not None:
@ -1211,7 +1211,7 @@ class AIAgent:
# (e.g. alibaba → DASHSCOPE_API_KEY, not ALIBABA_API_KEY).
_env_hint = f"{_explicit.upper()}_API_KEY"
try:
from hermes_cli.auth import PROVIDER_REGISTRY
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
_pcfg = PROVIDER_REGISTRY.get(_explicit)
if _pcfg and _pcfg.api_key_env_vars:
_env_hint = _pcfg.api_key_env_vars[0]
@ -1364,7 +1364,7 @@ class AIAgent:
self._cached_system_prompt: Optional[str] = None
# Filesystem checkpoint manager (transparent — not a tool)
from tools.checkpoint_manager import CheckpointManager
from hermes_agent.tools.checkpoint import CheckpointManager
self._checkpoint_mgr = CheckpointManager(
enabled=checkpoints_enabled,
max_snapshots=checkpoint_max_snapshots,
@ -1400,12 +1400,12 @@ class AIAgent:
)
# In-memory todo list for task planning (one per agent/session)
from tools.todo_tool import TodoStore
from hermes_agent.tools.todo import TodoStore
self._todo_store = TodoStore()
# Load config once for memory, skills, and compression sections
try:
from hermes_cli.config import load_config as _load_agent_config
from hermes_agent.cli.config import load_config as _load_agent_config
_agent_cfg = _load_agent_config()
except Exception:
_agent_cfg = {}
@ -1430,7 +1430,7 @@ class AIAgent:
self._memory_nudge_interval = int(mem_config.get("nudge_interval", 10))
self._memory_flush_min_turns = int(mem_config.get("flush_min_turns", 6))
if self._memory_enabled or self._user_profile_enabled:
from tools.memory_tool import MemoryStore
from hermes_agent.tools.memory import MemoryStore
self._memory_store = MemoryStore(
memory_char_limit=mem_config.get("memory_char_limit", 2200),
user_char_limit=mem_config.get("user_char_limit", 1375),
@ -1449,8 +1449,8 @@ class AIAgent:
_mem_provider_name = mem_config.get("provider", "") if mem_config else ""
if _mem_provider_name:
from agent.memory_manager import MemoryManager as _MemoryManager
from plugins.memory import load_memory_provider as _load_mem
from hermes_agent.agent.memory.manager import MemoryManager as _MemoryManager
from hermes_agent.plugins.memory import load_memory_provider as _load_mem
self._memory_manager = _MemoryManager()
_mp = _load_mem(_mem_provider_name)
if _mp and _mp.is_available():
@ -1479,7 +1479,7 @@ class AIAgent:
_init_kwargs["gateway_session_key"] = self._gateway_session_key
# Profile identity for per-profile provider scoping
try:
from hermes_cli.profiles import get_active_profile_name
from hermes_agent.cli.profiles import get_active_profile_name
_profile = get_active_profile_name()
_init_kwargs["agent_identity"] = _profile
_init_kwargs["agent_workspace"] = "hermes"
@ -1590,7 +1590,7 @@ class AIAgent:
# Check custom_providers per-model context_length
if _config_context_length is None:
try:
from hermes_cli.config import get_compatible_custom_providers
from hermes_agent.cli.config import get_compatible_custom_providers
_custom_providers = get_compatible_custom_providers(_agent_cfg)
except Exception:
_custom_providers = _agent_cfg.get("custom_providers")
@ -1641,7 +1641,7 @@ class AIAgent:
if _engine_name != "compressor":
# Try loading from plugins/context_engine/<name>/
try:
from plugins.context_engine import load_context_engine
from hermes_agent.plugins.context_engine import load_context_engine
_selected_engine = load_context_engine(_engine_name)
except Exception as _ce_load_err:
logger.debug("Context engine load from plugins/context_engine/: %s", _ce_load_err)
@ -1649,7 +1649,7 @@ class AIAgent:
# Try general plugin system as fallback
if _selected_engine is None:
try:
from hermes_cli.plugins import get_plugin_context_engine
from hermes_agent.cli.plugins import get_plugin_context_engine
_candidate = get_plugin_context_engine()
if _candidate and _candidate.name == _engine_name:
_selected_engine = _candidate
@ -1666,7 +1666,7 @@ class AIAgent:
if _selected_engine is not None:
self.context_compressor = _selected_engine
# Resolve context_length for plugin engines — mirrors switch_model() path
from agent.model_metadata import get_model_context_length
from hermes_agent.providers.metadata import get_model_context_length
_plugin_ctx_len = get_model_context_length(
self.model,
base_url=self.base_url,
@ -1702,7 +1702,7 @@ class AIAgent:
# Reject models whose context window is below the minimum required
# for reliable tool-calling workflows (64K tokens).
from agent.model_metadata import MINIMUM_CONTEXT_LENGTH
from hermes_agent.providers.metadata import MINIMUM_CONTEXT_LENGTH
_ctx = getattr(self.context_compressor, "context_length", 0)
if _ctx and _ctx < MINIMUM_CONTEXT_LENGTH:
raise ValueError(
@ -1879,7 +1879,7 @@ class AIAgent:
change persists across turns (unlike fallback which is
turn-scoped).
"""
from hermes_cli.providers import determine_api_mode
from hermes_agent.cli.providers import determine_api_mode
# ── Determine api_mode if not provided ──
if not api_mode:
@ -1911,7 +1911,7 @@ class AIAgent:
# ── Build new client ──
if api_mode == "anthropic_messages":
from agent.anthropic_adapter import (
from hermes_agent.providers.anthropic_adapter import (
build_anthropic_client,
resolve_anthropic_token,
_is_oauth_token,
@ -1959,7 +1959,7 @@ class AIAgent:
# ── Update context compressor ──
if hasattr(self, "context_compressor") and self.context_compressor:
from agent.model_metadata import get_model_context_length
from hermes_agent.providers.metadata import get_model_context_length
new_context_length = get_model_context_length(
self.model,
base_url=self.base_url,
@ -2154,8 +2154,8 @@ class AIAgent:
if not self.compression_enabled:
return
try:
from agent.auxiliary_client import get_text_auxiliary_client
from agent.model_metadata import (
from hermes_agent.providers.auxiliary import get_text_auxiliary_client
from hermes_agent.providers.metadata import (
MINIMUM_CONTEXT_LENGTH,
get_model_context_length,
)
@ -2448,7 +2448,7 @@ class AIAgent:
normalized_provider = (provider or "").strip().lower()
if normalized_provider == "copilot":
try:
from hermes_cli.models import _should_use_copilot_responses_api
from hermes_agent.cli.models.models import _should_use_copilot_responses_api
return _should_use_copilot_responses_api(model)
except Exception:
# Fall back to the generic GPT-5 rule if Copilot-specific
@ -3756,7 +3756,7 @@ class AIAgent:
if not headers:
return
try:
from agent.rate_limit_tracker import parse_rate_limit_headers
from hermes_agent.providers.rate_limiting import parse_rate_limit_headers
state = parse_rate_limit_headers(headers, provider=self.provider)
if state is not None:
self._rate_limit_state = state
@ -3888,7 +3888,7 @@ class AIAgent:
# 1. Kill background processes for this task
try:
from tools.process_registry import process_registry
from hermes_agent.tools.process_registry import process_registry
process_registry.kill_all(task_id=task_id)
except Exception:
pass
@ -4105,7 +4105,7 @@ class AIAgent:
if context_files_prompt:
prompt_parts.append(context_files_prompt)
from hermes_time import now as _hermes_now
from hermes_agent.time import now as _hermes_now
now = _hermes_now()
timestamp_line = f"Conversation started: {now.strftime('%A, %B %d, %Y %I:%M %p')}"
if self.pass_session_id and self.session_id:
@ -4233,7 +4233,7 @@ class AIAgent:
Returns the original list if no truncation was needed.
"""
from tools.delegate_tool import _get_max_concurrent_children
from hermes_agent.tools.delegate import _get_max_concurrent_children
max_children = _get_max_concurrent_children()
delegate_count = sum(1 for tc in tool_calls if tc.function.name == "delegate_task")
if delegate_count <= max_children:
@ -4409,7 +4409,7 @@ class AIAgent:
return None
def _create_openai_client(self, client_kwargs: dict, *, reason: str, shared: bool) -> Any:
from agent.auxiliary_client import _validate_base_url, _validate_proxy_env_urls
from hermes_agent.providers.auxiliary import _validate_base_url, _validate_proxy_env_urls
# Treat client_kwargs as read-only. Callers pass self._client_kwargs (or shallow
# copies of it) in; any in-place mutation leaks back into the stored dict and is
# reused on subsequent requests. #10933 hit this by injecting an httpx.Client
@ -4422,7 +4422,7 @@ class AIAgent:
_validate_proxy_env_urls()
_validate_base_url(client_kwargs.get("base_url"))
if self.provider == "copilot-acp" or str(client_kwargs.get("base_url", "")).startswith("acp://copilot"):
from agent.copilot_acp_client import CopilotACPClient
from hermes_agent.agent.copilot_acp_client import CopilotACPClient
client = CopilotACPClient(**client_kwargs)
logger.info(
@ -4433,7 +4433,7 @@ class AIAgent:
)
return client
if self.provider == "google-gemini-cli" or str(client_kwargs.get("base_url", "")).startswith("cloudcode-pa://"):
from agent.gemini_cloudcode_adapter import GeminiCloudCodeClient
from hermes_agent.providers.gemini_cloudcode_adapter import GeminiCloudCodeClient
# Strip OpenAI-specific kwargs the Gemini client doesn't accept
safe_kwargs = {
@ -4449,7 +4449,7 @@ class AIAgent:
)
return client
if self.provider == "gemini":
from agent.gemini_native_adapter import GeminiNativeClient, is_native_gemini_base_url
from hermes_agent.providers.gemini_adapter import GeminiNativeClient, is_native_gemini_base_url
base_url = str(client_kwargs.get("base_url", "") or "")
if is_native_gemini_base_url(base_url):
@ -4904,7 +4904,7 @@ class AIAgent:
return False
try:
from hermes_cli.auth import resolve_codex_runtime_credentials
from hermes_agent.cli.auth.auth import resolve_codex_runtime_credentials
creds = resolve_codex_runtime_credentials(force_refresh=force)
except Exception as exc:
@ -4933,7 +4933,7 @@ class AIAgent:
return False
try:
from hermes_cli.auth import resolve_nous_runtime_credentials
from hermes_agent.cli.auth.auth import resolve_nous_runtime_credentials
creds = resolve_nous_runtime_credentials(
min_key_ttl_seconds=max(60, int(os.getenv("HERMES_NOUS_MIN_KEY_TTL_SECONDS", "1800"))),
@ -4972,7 +4972,7 @@ class AIAgent:
return False
try:
from agent.anthropic_adapter import resolve_anthropic_token, build_anthropic_client
from hermes_agent.providers.anthropic_adapter import resolve_anthropic_token, build_anthropic_client
new_token = resolve_anthropic_token()
except Exception as exc:
@ -5005,19 +5005,19 @@ class AIAgent:
# Only treat as OAuth on native Anthropic; third-party endpoints using
# the Anthropic protocol must not trip OAuth paths (#1739 & third-party
# identity-injection guard).
from agent.anthropic_adapter import _is_oauth_token
from hermes_agent.providers.anthropic_adapter import _is_oauth_token
self._is_anthropic_oauth = _is_oauth_token(new_token) if self.provider == "anthropic" else False
return True
def _apply_client_headers_for_base_url(self, base_url: str) -> None:
from agent.auxiliary_client import _AI_GATEWAY_HEADERS, _OR_HEADERS
from hermes_agent.providers.auxiliary import _AI_GATEWAY_HEADERS, _OR_HEADERS
if base_url_host_matches(base_url, "openrouter.ai"):
self._client_kwargs["default_headers"] = dict(_OR_HEADERS)
elif base_url_host_matches(base_url, "ai-gateway.vercel.sh"):
self._client_kwargs["default_headers"] = dict(_AI_GATEWAY_HEADERS)
elif base_url_host_matches(base_url, "api.githubcopilot.com"):
from hermes_cli.models import copilot_default_headers
from hermes_agent.cli.models.models import copilot_default_headers
self._client_kwargs["default_headers"] = copilot_default_headers()
elif base_url_host_matches(base_url, "api.kimi.com"):
@ -5025,7 +5025,7 @@ class AIAgent:
elif base_url_host_matches(base_url, "portal.qwen.ai"):
self._client_kwargs["default_headers"] = _qwen_portal_headers()
elif base_url_host_matches(base_url, "chatgpt.com"):
from agent.auxiliary_client import _codex_cloudflare_headers
from hermes_agent.providers.auxiliary import _codex_cloudflare_headers
self._client_kwargs["default_headers"] = _codex_cloudflare_headers(
self._client_kwargs.get("api_key", "")
)
@ -5037,7 +5037,7 @@ class AIAgent:
runtime_base = getattr(entry, "runtime_base_url", None) or getattr(entry, "base_url", None) or self.base_url
if self.api_mode == "anthropic_messages":
from agent.anthropic_adapter import build_anthropic_client, _is_oauth_token
from hermes_agent.providers.anthropic_adapter import build_anthropic_client, _is_oauth_token
try:
self._anthropic_client.close()
@ -5181,7 +5181,7 @@ class AIAgent:
result["response"] = self._anthropic_messages_create(api_kwargs)
elif self.api_mode == "bedrock_converse":
# Bedrock uses boto3 directly — no OpenAI client needed.
from agent.bedrock_adapter import (
from hermes_agent.providers.bedrock_adapter import (
_get_bedrock_runtime_client,
normalize_converse_response,
)
@ -5246,7 +5246,7 @@ class AIAgent:
)
try:
if self.api_mode == "anthropic_messages":
from agent.anthropic_adapter import build_anthropic_client
from hermes_agent.providers.anthropic_adapter import build_anthropic_client
self._anthropic_client.close()
self._anthropic_client = build_anthropic_client(
@ -5278,7 +5278,7 @@ class AIAgent:
# seed future retries.
try:
if self.api_mode == "anthropic_messages":
from agent.anthropic_adapter import build_anthropic_client
from hermes_agent.providers.anthropic_adapter import build_anthropic_client
self._anthropic_client.close()
self._anthropic_client = build_anthropic_client(
@ -5439,7 +5439,7 @@ class AIAgent:
def _bedrock_call():
try:
from agent.bedrock_adapter import (
from hermes_agent.providers.bedrock_adapter import (
_get_bedrock_runtime_client,
stream_converse_with_callbacks,
)
@ -6019,7 +6019,7 @@ class AIAgent:
if self._interrupt_requested:
try:
if self.api_mode == "anthropic_messages":
from agent.anthropic_adapter import build_anthropic_client
from hermes_agent.providers.anthropic_adapter import build_anthropic_client
self._anthropic_client.close()
self._anthropic_client = build_anthropic_client(
@ -6129,7 +6129,7 @@ class AIAgent:
# raw_codex=True because the main agent needs direct responses.stream()
# access for Codex providers.
try:
from agent.auxiliary_client import resolve_provider_client
from hermes_agent.providers.auxiliary import resolve_provider_client
# Pass base_url and api_key from fallback config so custom
# endpoints (e.g. Ollama Cloud) resolve correctly instead of
# falling through to OpenRouter defaults.
@ -6150,7 +6150,7 @@ class AIAgent:
fb_provider)
return self._try_activate_fallback() # try next in chain
try:
from hermes_cli.model_normalize import normalize_model_for_provider
from hermes_agent.cli.models.normalize import normalize_model_for_provider
fb_model = normalize_model_for_provider(fb_model, fb_provider)
except Exception:
@ -6193,7 +6193,7 @@ class AIAgent:
if fb_api_mode == "anthropic_messages":
# Build native Anthropic client instead of using OpenAI client
from agent.anthropic_adapter import build_anthropic_client, resolve_anthropic_token, _is_oauth_token
from hermes_agent.providers.anthropic_adapter import build_anthropic_client, resolve_anthropic_token, _is_oauth_token
effective_key = (fb_client.api_key or resolve_anthropic_token() or "") if fb_provider == "anthropic" else (fb_client.api_key or "")
self.api_key = effective_key
self._anthropic_api_key = effective_key
@ -6246,7 +6246,7 @@ class AIAgent:
# context window (e.g. 200K) instead of the fallback's (e.g. 32K),
# causing oversized sessions to overflow the fallback.
if hasattr(self, 'context_compressor') and self.context_compressor:
from agent.model_metadata import get_model_context_length
from hermes_agent.providers.metadata import get_model_context_length
fb_context_length = get_model_context_length(
self.model, base_url=self.base_url,
api_key=self.api_key, provider=self.provider,
@ -6307,7 +6307,7 @@ class AIAgent:
# ── Rebuild client for the primary provider ──
if self.api_mode == "anthropic_messages":
from agent.anthropic_adapter import build_anthropic_client
from hermes_agent.providers.anthropic_adapter import build_anthropic_client
self._anthropic_api_key = rt["anthropic_api_key"]
self._anthropic_base_url = rt["anthropic_base_url"]
self._anthropic_client = build_anthropic_client(
@ -6404,7 +6404,7 @@ class AIAgent:
self.api_key = rt["api_key"]
if self.api_mode == "anthropic_messages":
from agent.anthropic_adapter import build_anthropic_client
from hermes_agent.providers.anthropic_adapter import build_anthropic_client
self._anthropic_api_key = rt["anthropic_api_key"]
self._anthropic_base_url = rt["anthropic_base_url"]
self._anthropic_client = build_anthropic_client(
@ -6487,7 +6487,7 @@ class AIAgent:
description = ""
try:
from tools.vision_tools import vision_analyze_tool
from hermes_agent.tools.vision import vision_analyze_tool
result_json = asyncio.run(
vision_analyze_tool(image_url=vision_source, user_prompt=analysis_prompt)
@ -6563,7 +6563,7 @@ class AIAgent:
"""Return the cached AnthropicTransport instance (lazy singleton)."""
t = getattr(self, "_anthropic_transport", None)
if t is None:
from agent.transports import get_transport
from hermes_agent.providers import get_transport
t = get_transport("anthropic_messages")
self._anthropic_transport = t
return t
@ -6572,7 +6572,7 @@ class AIAgent:
"""Return the cached ResponsesApiTransport instance (lazy singleton)."""
t = getattr(self, "_codex_transport", None)
if t is None:
from agent.transports import get_transport
from hermes_agent.providers import get_transport
t = get_transport("codex_responses")
self._codex_transport = t
return t
@ -6581,7 +6581,7 @@ class AIAgent:
"""Return the cached ChatCompletionsTransport instance (lazy singleton)."""
t = getattr(self, "_chat_completions_transport", None)
if t is None:
from agent.transports import get_transport
from hermes_agent.providers import get_transport
t = get_transport("chat_completions")
self._chat_completions_transport = t
return t
@ -6590,7 +6590,7 @@ class AIAgent:
"""Return the cached BedrockTransport instance (lazy singleton)."""
t = getattr(self, "_bedrock_transport", None)
if t is None:
from agent.transports import get_transport
from hermes_agent.providers import get_transport
t = get_transport("bedrock_converse")
self._bedrock_transport = t
return t
@ -6795,7 +6795,7 @@ class AIAgent:
# Temperature: _fixed_temperature_for_model may return OMIT_TEMPERATURE
# sentinel (temperature omitted entirely), a numeric override, or None.
try:
from agent.auxiliary_client import _fixed_temperature_for_model, OMIT_TEMPERATURE
from hermes_agent.providers.auxiliary import _fixed_temperature_for_model, OMIT_TEMPERATURE
_ft = _fixed_temperature_for_model(self.model, self.base_url)
_omit_temp = _ft is OMIT_TEMPERATURE
_fixed_temp = _ft if not _omit_temp else None
@ -6822,7 +6822,7 @@ class AIAgent:
_ant_max = None
if (_is_or or _is_nous) and "claude" in (self.model or "").lower():
try:
from agent.anthropic_adapter import _get_anthropic_max_output
from hermes_agent.providers.anthropic_adapter import _get_anthropic_max_output
_ant_max = _get_anthropic_max_output(self.model)
except Exception:
pass # fail open — let the proxy pick its default
@ -6888,7 +6888,7 @@ class AIAgent:
or base_url_host_matches(self._base_url_lower, "api.githubcopilot.com")
):
try:
from hermes_cli.models import github_model_reasoning_efforts
from hermes_agent.cli.models.models import github_model_reasoning_efforts
return bool(github_model_reasoning_efforts(self.model))
except Exception:
@ -6912,7 +6912,7 @@ class AIAgent:
def _github_models_reasoning_extra_body(self) -> dict | None:
"""Format reasoning payload for GitHub Models/OpenAI-compatible routes."""
try:
from hermes_cli.models import github_model_reasoning_efforts
from hermes_agent.cli.models.models import github_model_reasoning_efforts
except Exception:
return None
@ -7191,7 +7191,7 @@ class AIAgent:
# Use auxiliary client for the flush call when available --
# it's cheaper and avoids Codex Responses API incompatibility.
from agent.auxiliary_client import (
from hermes_agent.providers.auxiliary import (
call_llm as _call_llm,
_fixed_temperature_for_model,
OMIT_TEMPERATURE,
@ -7254,7 +7254,7 @@ class AIAgent:
}
if _flush_temperature is not None:
api_kwargs["temperature"] = _flush_temperature
from agent.auxiliary_client import _get_task_timeout
from hermes_agent.providers.auxiliary import _get_task_timeout
response = self._ensure_primary_openai_client(reason="flush_memories").chat.completions.create(
**api_kwargs, timeout=_get_task_timeout("flush_memories")
)
@ -7291,7 +7291,7 @@ class AIAgent:
try:
args = json.loads(tc.function.arguments)
flush_target = args.get("target", "memory")
from tools.memory_tool import memory_tool as _memory_tool
from hermes_agent.tools.memory import memory_tool as _memory_tool
_memory_tool(
action=args.get("action"),
target=flush_target,
@ -7405,7 +7405,7 @@ class AIAgent:
# read content is summarised away — if the model re-reads the same
# file it needs the full content, not a "file unchanged" stub.
try:
from tools.file_tools import reset_file_dedup
from hermes_agent.tools.files.tools import reset_file_dedup
reset_file_dedup(task_id)
except Exception:
pass
@ -7446,7 +7446,7 @@ class AIAgent:
New DELEGATE_TASK_SCHEMA fields only need to be added here to reach all
invocation paths (concurrent, sequential, inline).
"""
from tools.delegate_tool import delegate_task as _delegate_task
from hermes_agent.tools.delegate import delegate_task as _delegate_task
return _delegate_task(
goal=function_args.get("goal"),
context=function_args.get("context"),
@ -7470,7 +7470,7 @@ class AIAgent:
# Check plugin hooks for a block directive before executing anything.
block_message: Optional[str] = None
try:
from hermes_cli.plugins import get_pre_tool_call_block_message
from hermes_agent.cli.plugins import get_pre_tool_call_block_message
block_message = get_pre_tool_call_block_message(
function_name, function_args, task_id=effective_task_id or "",
)
@ -7480,7 +7480,7 @@ class AIAgent:
return json.dumps({"error": block_message}, ensure_ascii=False)
if function_name == "todo":
from tools.todo_tool import todo_tool as _todo_tool
from hermes_agent.tools.todo import todo_tool as _todo_tool
return _todo_tool(
todos=function_args.get("todos"),
merge=function_args.get("merge", False),
@ -7489,7 +7489,7 @@ class AIAgent:
elif function_name == "session_search":
if not self._session_db:
return json.dumps({"success": False, "error": "Session database not available."})
from tools.session_search_tool import session_search as _session_search
from hermes_agent.tools.session_search import session_search as _session_search
return _session_search(
query=function_args.get("query", ""),
role_filter=function_args.get("role_filter"),
@ -7499,7 +7499,7 @@ class AIAgent:
)
elif function_name == "memory":
target = function_args.get("target", "memory")
from tools.memory_tool import memory_tool as _memory_tool
from hermes_agent.tools.memory import memory_tool as _memory_tool
result = _memory_tool(
action=function_args.get("action"),
target=target,
@ -7521,7 +7521,7 @@ class AIAgent:
elif self._memory_manager and self._memory_manager.has_tool(function_name):
return self._memory_manager.handle_tool_call(function_name, function_args)
elif function_name == "clarify":
from tools.clarify_tool import clarify_tool as _clarify_tool
from hermes_agent.tools.clarify import clarify_tool as _clarify_tool
return _clarify_tool(
question=function_args.get("question", ""),
choices=function_args.get("choices"),
@ -7684,7 +7684,7 @@ class AIAgent:
# The callback is thread-local; the main thread's callback
# is invisible to worker threads.
try:
from tools.environments.base import set_activity_callback
from hermes_agent.backends.base import set_activity_callback
set_activity_callback(self._touch_activity)
except Exception:
pass
@ -7899,7 +7899,7 @@ class AIAgent:
# Check plugin hooks for a block directive before executing.
_block_msg: Optional[str] = None
try:
from hermes_cli.plugins import get_pre_tool_call_block_message
from hermes_agent.cli.plugins import get_pre_tool_call_block_message
_block_msg = get_pre_tool_call_block_message(
function_name, function_args, task_id=effective_task_id or "",
)
@ -7935,7 +7935,7 @@ class AIAgent:
# the agent while a command is running.
if _block_msg is None:
try:
from tools.environments.base import set_activity_callback
from hermes_agent.backends.base import set_activity_callback
set_activity_callback(self._touch_activity)
except Exception:
pass
@ -7984,7 +7984,7 @@ class AIAgent:
function_result = json.dumps({"error": _block_msg}, ensure_ascii=False)
tool_duration = 0.0
elif function_name == "todo":
from tools.todo_tool import todo_tool as _todo_tool
from hermes_agent.tools.todo import todo_tool as _todo_tool
function_result = _todo_tool(
todos=function_args.get("todos"),
merge=function_args.get("merge", False),
@ -7997,7 +7997,7 @@ class AIAgent:
if not self._session_db:
function_result = json.dumps({"success": False, "error": "Session database not available."})
else:
from tools.session_search_tool import session_search as _session_search
from hermes_agent.tools.session_search import session_search as _session_search
function_result = _session_search(
query=function_args.get("query", ""),
role_filter=function_args.get("role_filter"),
@ -8010,7 +8010,7 @@ class AIAgent:
self._vprint(f" {_get_cute_tool_message_impl('session_search', function_args, tool_duration, result=function_result)}")
elif function_name == "memory":
target = function_args.get("target", "memory")
from tools.memory_tool import memory_tool as _memory_tool
from hermes_agent.tools.memory import memory_tool as _memory_tool
function_result = _memory_tool(
action=function_args.get("action"),
target=target,
@ -8032,7 +8032,7 @@ class AIAgent:
if self._should_emit_quiet_tool_messages():
self._vprint(f" {_get_cute_tool_message_impl('memory', function_args, tool_duration, result=function_result)}")
elif function_name == "clarify":
from tools.clarify_tool import clarify_tool as _clarify_tool
from hermes_agent.tools.clarify import clarify_tool as _clarify_tool
function_result = _clarify_tool(
question=function_args.get("question", ""),
choices=function_args.get("choices"),
@ -8287,7 +8287,7 @@ class AIAgent:
summary_extra_body = {}
try:
from agent.auxiliary_client import _fixed_temperature_for_model, OMIT_TEMPERATURE as _OMIT_TEMP
from hermes_agent.providers.auxiliary import _fixed_temperature_for_model, OMIT_TEMPERATURE as _OMIT_TEMP
except Exception:
_fixed_temperature_for_model = None
_OMIT_TEMP = None
@ -8454,7 +8454,7 @@ class AIAgent:
# Tag all log records on this thread with the session ID so
# ``hermes logs --session <id>`` can filter a single conversation.
from hermes_logging import set_session_context
from hermes_agent.logging import set_session_context
set_session_context(self.session_id)
# If the previous turn activated fallback, restore the primary
@ -8616,7 +8616,7 @@ class AIAgent:
# continuation). Plugins can use this to initialise
# session-scoped state (e.g. warm a memory cache).
try:
from hermes_cli.plugins import invoke_hook as _invoke_hook
from hermes_agent.cli.plugins import invoke_hook as _invoke_hook
_invoke_hook(
"on_session_start",
session_id=self.session_id,
@ -8717,7 +8717,7 @@ class AIAgent:
# All injected context is ephemeral (not persisted to session DB).
_plugin_user_context = ""
try:
from hermes_cli.plugins import invoke_hook as _invoke_hook
from hermes_agent.cli.plugins import invoke_hook as _invoke_hook
_pre_results = _invoke_hook(
"pre_llm_call",
session_id=self.session_id,
@ -9091,7 +9091,7 @@ class AIAgent:
# deepens the rate limit hole.
if self.provider == "nous":
try:
from agent.nous_rate_guard import (
from hermes_agent.providers.nous_rate_guard import (
nous_rate_limit_remaining,
format_remaining as _fmt_nous_remaining,
)
@ -9140,7 +9140,7 @@ class AIAgent:
api_kwargs = self._get_codex_transport().preflight_kwargs(api_kwargs, allow_stream=False)
try:
from hermes_cli.plugins import invoke_hook as _invoke_hook
from hermes_agent.cli.plugins import invoke_hook as _invoke_hook
_invoke_hook(
"pre_api_request",
task_id=effective_task_id,
@ -9765,7 +9765,7 @@ class AIAgent:
# resume hitting Nous.
if self.provider == "nous":
try:
from agent.nous_rate_guard import clear_nous_rate_limit
from hermes_agent.providers.nous_rate_guard import clear_nous_rate_limit
clear_nous_rate_limit()
except Exception:
pass
@ -10013,7 +10013,7 @@ class AIAgent:
and not anthropic_auth_retry_attempted
):
anthropic_auth_retry_attempted = True
from agent.anthropic_adapter import _is_oauth_token
from hermes_agent.providers.anthropic_adapter import _is_oauth_token
if self._try_refresh_anthropic_client_credentials():
print(f"{self.log_prefix}🔐 Anthropic credentials refreshed after 401. Retrying request...")
continue
@ -10024,7 +10024,7 @@ class AIAgent:
print(f"{self.log_prefix} Auth method: {auth_method}")
print(f"{self.log_prefix} Token prefix: {str(key)[:12]}..." if key and len(str(key)) > 12 else f"{self.log_prefix} Token: (empty or short)")
print(f"{self.log_prefix} Troubleshooting:")
from hermes_constants import display_hermes_home as _dhh_fn
from hermes_agent.constants import display_hermes_home as _dhh_fn
_dhh = _dhh_fn()
print(f"{self.log_prefix} • Check ANTHROPIC_TOKEN in {_dhh}/.env for Hermes-managed OAuth/setup tokens")
print(f"{self.log_prefix} • Check ANTHROPIC_API_KEY in {_dhh}/.env for API keys or legacy token values")
@ -10233,7 +10233,7 @@ class AIAgent:
and not recovered_with_pool
):
try:
from agent.nous_rate_guard import record_nous_rate_limit
from hermes_agent.providers.nous_rate_guard import record_nous_rate_limit
_err_resp = getattr(api_error, "response", None)
_err_hdrs = (
getattr(_err_resp, "headers", None)
@ -10776,7 +10776,7 @@ class AIAgent:
assistant_message.content = str(raw)
try:
from hermes_cli.plugins import invoke_hook as _invoke_hook
from hermes_agent.cli.plugins import invoke_hook as _invoke_hook
_assistant_tool_calls = getattr(assistant_message, "tool_calls", None) or []
_assistant_text = assistant_message.content or ""
_invoke_hook(
@ -11642,7 +11642,7 @@ class AIAgent:
# to an external memory system).
if final_response and not interrupted:
try:
from hermes_cli.plugins import invoke_hook as _invoke_hook
from hermes_agent.cli.plugins import invoke_hook as _invoke_hook
_invoke_hook(
"post_llm_call",
session_id=self.session_id,
@ -11747,7 +11747,7 @@ class AIAgent:
# Fired at the very end of every run_conversation call.
# Plugins can use this for cleanup, flushing buffers, etc.
try:
from hermes_cli.plugins import invoke_hook as _invoke_hook
from hermes_agent.cli.plugins import invoke_hook as _invoke_hook
_invoke_hook(
"on_session_end",
session_id=self.session_id,
@ -11817,8 +11817,8 @@ def main(
# Handle tool listing
if list_tools:
from model_tools import get_all_tool_names, get_available_toolsets
from toolsets import get_all_toolsets, get_toolset_info
from hermes_agent.tools.dispatch import get_all_tool_names, get_available_toolsets
from hermes_agent.tools.toolsets import get_all_toolsets, get_toolset_info
print("📋 Available Tools & Toolsets:")
print("-" * 50)

View file

@ -33,8 +33,8 @@ import logging
import re
from typing import Any, Dict, List, Optional
from agent.memory_provider import MemoryProvider
from tools.registry import tool_error
from hermes_agent.agent.memory.provider import MemoryProvider
from hermes_agent.tools.registry import tool_error
logger = logging.getLogger(__name__)
@ -361,7 +361,7 @@ class MemoryManager:
``get_hermes_home()`` themselves.
"""
if "hermes_home" not in kwargs:
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
kwargs["hermes_home"] = str(get_hermes_home())
for provider in self._providers:
try:

View file

@ -12,10 +12,10 @@ import threading
from collections import OrderedDict
from pathlib import Path
from hermes_constants import get_hermes_home, get_skills_dir, is_wsl
from hermes_agent.constants import get_hermes_home, get_skills_dir, is_wsl
from typing import Optional
from agent.skill_utils import (
from hermes_agent.agent.skill_utils import (
extract_skill_conditions,
extract_skill_description,
get_all_skills_dirs,
@ -24,7 +24,7 @@ from agent.skill_utils import (
parse_frontmatter,
skill_matches_platform,
)
from utils import atomic_json_write
from hermes_agent.utils import atomic_json_write
logger = logging.getLogger(__name__)
@ -619,7 +619,7 @@ def build_skills_system_prompt(
# ── Layer 1: in-process LRU cache ─────────────────────────────────
# Include the resolved platform so per-platform disabled-skill lists
# produce distinct cache entries (gateway serves multiple platforms).
from gateway.session_context import get_session_env
from hermes_agent.gateway.session_context import get_session_env
_platform_hint = (
os.environ.get("HERMES_PLATFORM")
or get_session_env("HERMES_SESSION_PLATFORM")
@ -824,8 +824,8 @@ def build_skills_system_prompt(
def build_nous_subscription_prompt(valid_tool_names: "set[str] | None" = None) -> str:
"""Build a compact Nous subscription capability block for the system prompt."""
try:
from hermes_cli.nous_subscription import get_nous_subscription_features
from tools.tool_backend_helpers import managed_nous_tools_enabled
from hermes_agent.cli.nous_subscription import get_nous_subscription_features
from hermes_agent.tools.backend_helpers import managed_nous_tools_enabled
except Exception as exc:
logger.debug("Failed to import Nous subscription helper: %s", exc)
return ""
@ -911,7 +911,7 @@ def load_soul_md() -> Optional[str]:
``skip_soul=True`` so SOUL.md isn't injected twice.
"""
try:
from hermes_cli.config import ensure_hermes_home
from hermes_agent.cli.config import ensure_hermes_home
ensure_hermes_home()
except Exception as e:
logger.debug("Could not ensure HERMES_HOME before loading SOUL.md: %s", e)

View file

@ -75,7 +75,7 @@ try:
except ImportError: # pragma: no cover
fcntl = None # type: ignore[assignment]
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
logger = logging.getLogger(__name__)
@ -177,7 +177,7 @@ def register_from_config(
registered: List[ShellHookSpec] = []
# Import lazily — avoids circular imports at module-load time.
from hermes_cli.plugins import get_plugin_manager
from hermes_agent.cli.plugins import get_plugin_manager
manager = get_plugin_manager()
@ -243,7 +243,7 @@ def _parse_hooks_block(hooks_cfg: Any) -> List[ShellHookSpec]:
Malformed entries warn-and-skip we never raise from config parsing
because a broken hook must not crash the agent.
"""
from hermes_cli.plugins import VALID_HOOKS
from hermes_agent.cli.plugins import VALID_HOOKS
if not isinstance(hooks_cfg, dict):
return []

View file

@ -13,7 +13,7 @@ from datetime import datetime
from pathlib import Path
from typing import Any, Dict, Optional
from hermes_constants import display_hermes_home
from hermes_agent.constants import display_hermes_home
logger = logging.getLogger(__name__)
@ -39,7 +39,7 @@ _INLINE_SHELL_MAX_OUTPUT = 4000
def _load_skills_config() -> dict:
"""Load the ``skills`` section of config.yaml (best-effort)."""
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
cfg = load_config() or {}
skills_cfg = cfg.get("skills")
@ -156,7 +156,7 @@ def _load_skill_payload(skill_identifier: str, task_id: str | None = None) -> tu
return None
try:
from tools.skills_tool import SKILLS_DIR, skill_view
from hermes_agent.tools.skills.tool import SKILLS_DIR, skill_view
identifier_path = Path(raw_identifier).expanduser()
if identifier_path.is_absolute():
@ -202,7 +202,7 @@ def _inject_skill_config(loaded_skill: dict[str, Any], parts: list[str]) -> None
without needing to read config.yaml itself.
"""
try:
from agent.skill_utils import (
from hermes_agent.agent.skill_utils import (
extract_skill_config_vars,
parse_frontmatter,
resolve_skill_config_values,
@ -241,7 +241,7 @@ def _build_skill_message(
session_id: str | None = None,
) -> str:
"""Format a loaded skill into a user/system message payload."""
from tools.skills_tool import SKILLS_DIR
from hermes_agent.tools.skills.tool import SKILLS_DIR
content = str(loaded_skill.get("content") or "")
@ -344,8 +344,8 @@ def scan_skill_commands() -> Dict[str, Dict[str, Any]]:
global _skill_commands
_skill_commands = {}
try:
from tools.skills_tool import SKILLS_DIR, _parse_frontmatter, skill_matches_platform, _get_disabled_skill_names
from agent.skill_utils import get_external_skills_dirs
from hermes_agent.tools.skills.tool import SKILLS_DIR, _parse_frontmatter, skill_matches_platform, _get_disabled_skill_names
from hermes_agent.agent.skill_utils import get_external_skills_dirs
disabled = _get_disabled_skill_names()
seen_names: set = set()

View file

@ -12,7 +12,7 @@ import sys
from pathlib import Path
from typing import Any, Dict, List, Optional, Set, Tuple
from hermes_constants import get_config_path, get_skills_dir
from hermes_agent.constants import get_config_path, get_skills_dir
logger = logging.getLogger(__name__)
@ -145,7 +145,7 @@ def get_disabled_skill_names(platform: str | None = None) -> Set[str]:
if not isinstance(skills_cfg, dict):
return set()
from gateway.session_context import get_session_env
from hermes_agent.gateway.session_context import get_session_env
resolved_platform = (
platform
or os.getenv("HERMES_PLATFORM")

View file

@ -19,7 +19,7 @@ import shlex
from pathlib import Path
from typing import Dict, Any, Optional, Set
from agent.prompt_builder import _scan_context_content
from hermes_agent.agent.prompt_builder import _scan_context_content
logger = logging.getLogger(__name__)

View file

@ -8,7 +8,7 @@ import logging
import threading
from typing import Optional
from agent.auxiliary_client import call_llm
from hermes_agent.providers.auxiliary import call_llm
logger = logging.getLogger(__name__)

View file

@ -8,6 +8,6 @@ The terminal_tool.py factory (_create_environment) selects the backend
based on the TERMINAL_ENV configuration.
"""
from tools.environments.base import BaseEnvironment
from hermes_agent.backends.base import BaseEnvironment
__all__ = ["BaseEnvironment"]

View file

@ -20,8 +20,8 @@ from abc import ABC, abstractmethod
from pathlib import Path
from typing import IO, Callable, Protocol
from hermes_constants import get_hermes_home
from tools.interrupt import is_interrupted
from hermes_agent.constants import get_hermes_home
from hermes_agent.tools.interrupt import is_interrupted
logger = logging.getLogger(__name__)
@ -710,7 +710,7 @@ class BaseEnvironment(ABC):
# server, `yes > /dev/null`, etc.), leaking the subshell forever.
# Rewriting to `A && { B & }` runs B as a plain background in the
# current shell — no subshell wait.
from tools.terminal_tool import _rewrite_compound_background
from hermes_agent.tools.terminal import _rewrite_compound_background
exec_command = _rewrite_compound_background(exec_command)
effective_timeout = timeout or self.timeout
effective_cwd = cwd or self.cwd
@ -757,7 +757,7 @@ class BaseEnvironment(ABC):
def _prepare_command(self, command: str) -> tuple[str | None, str | None]:
"""Transform sudo commands if SUDO_PASSWORD is available."""
from tools.terminal_tool import _transform_sudo_command
from hermes_agent.tools.terminal import _transform_sudo_command
return _transform_sudo_command(command)

View file

@ -12,11 +12,11 @@ import shlex
import threading
from pathlib import Path
from tools.environments.base import (
from hermes_agent.backends.base import (
BaseEnvironment,
_ThreadedProcessHandle,
)
from tools.environments.file_sync import (
from hermes_agent.backends.file_sync import (
FileSyncManager,
iter_sync_files,
quoted_mkdir_command,

View file

@ -14,8 +14,8 @@ import sys
import uuid
from typing import Optional
from tools.environments.base import BaseEnvironment, _popen_bash
from tools.environments.local import _HERMES_PROVIDER_ENV_BLOCKLIST
from hermes_agent.backends.base import BaseEnvironment, _popen_bash
from hermes_agent.backends.local import _HERMES_PROVIDER_ENV_BLOCKLIST
logger = logging.getLogger(__name__)
@ -91,7 +91,7 @@ def _normalize_env_dict(env: dict | None) -> dict[str, str]:
def _load_hermes_env_vars() -> dict[str, str]:
"""Load ~/.hermes/.env values without failing Docker command execution."""
try:
from hermes_cli.config import load_env
from hermes_agent.cli.config import load_env
return load_env() or {}
except Exception:
@ -298,7 +298,7 @@ class DockerEnvironment(BaseEnvironment):
# Persistent workspace via bind mounts from a configurable host directory
# (TERMINAL_SANDBOX_DIR, default ~/.hermes/sandboxes/). Non-persistent
# mode uses tmpfs (ephemeral, fast, gone on cleanup).
from tools.environments.base import get_sandbox_dir
from hermes_agent.backends.base import get_sandbox_dir
# User-configured volume mounts (from config.yaml docker_volumes)
volume_args = []
@ -362,7 +362,7 @@ class DockerEnvironment(BaseEnvironment):
# Mount credential files (OAuth tokens, etc.) declared by skills.
# Read-only so the container can authenticate but not modify host creds.
try:
from tools.credential_files import (
from hermes_agent.tools.credential_files import (
get_credential_file_mounts,
get_skills_directory_mount,
get_cache_directory_mounts,
@ -464,7 +464,7 @@ class DockerEnvironment(BaseEnvironment):
explicit_forward_keys = set(self._forward_env)
passthrough_keys: set[str] = set()
try:
from tools.env_passthrough import get_all_passthrough
from hermes_agent.tools.env_passthrough import get_all_passthrough
passthrough_keys = set(get_all_passthrough())
except Exception:
pass

View file

@ -24,8 +24,8 @@ except ImportError:
from pathlib import Path
from typing import Callable
from hermes_constants import get_hermes_home
from tools.environments.base import _file_mtime_key
from hermes_agent.constants import get_hermes_home
from hermes_agent.backends.base import _file_mtime_key
logger = logging.getLogger(__name__)
@ -50,7 +50,7 @@ def iter_sync_files(container_base: str = "/root/.hermes") -> list[tuple[str, st
"""
# Late import: credential_files imports agent modules that create
# circular dependencies if loaded at file_sync module level.
from tools.credential_files import (
from hermes_agent.tools.credential_files import (
get_credential_file_mounts,
iter_cache_files,
iter_skills_files,

View file

@ -7,7 +7,7 @@ import signal
import subprocess
import tempfile
from tools.environments.base import BaseEnvironment, _pipe_stdin
from hermes_agent.backends.base import BaseEnvironment, _pipe_stdin
_IS_WINDOWS = platform.system() == "Windows"
@ -21,7 +21,7 @@ def _build_provider_env_blocklist() -> frozenset:
blocked: set[str] = set()
try:
from hermes_cli.auth import PROVIDER_REGISTRY
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
for pconfig in PROVIDER_REGISTRY.values():
blocked.update(pconfig.api_key_env_vars)
if pconfig.base_url_env_var:
@ -30,7 +30,7 @@ def _build_provider_env_blocklist() -> frozenset:
pass
try:
from hermes_cli.config import OPTIONAL_ENV_VARS
from hermes_agent.cli.config import OPTIONAL_ENV_VARS
for name, metadata in OPTIONAL_ENV_VARS.items():
category = metadata.get("category")
if category in {"tool", "messaging"}:
@ -110,7 +110,7 @@ _HERMES_PROVIDER_ENV_BLOCKLIST = _build_provider_env_blocklist()
def _sanitize_subprocess_env(base_env: dict | None, extra_env: dict | None = None) -> dict:
"""Filter Hermes-managed secrets from a subprocess environment."""
try:
from tools.env_passthrough import is_env_passthrough as _is_passthrough
from hermes_agent.tools.env_passthrough import is_env_passthrough as _is_passthrough
except Exception:
_is_passthrough = lambda _: False # noqa: E731
@ -130,7 +130,7 @@ def _sanitize_subprocess_env(base_env: dict | None, extra_env: dict | None = Non
sanitized[key] = value
# Per-profile HOME isolation for background processes (same as _make_run_env).
from hermes_constants import get_subprocess_home
from hermes_agent.constants import get_subprocess_home
_profile_home = get_subprocess_home()
if _profile_home:
sanitized["HOME"] = _profile_home
@ -186,7 +186,7 @@ _SANE_PATH = (
def _make_run_env(env: dict) -> dict:
"""Build a run environment with a sane PATH and provider-var stripping."""
try:
from tools.env_passthrough import is_env_passthrough as _is_passthrough
from hermes_agent.tools.env_passthrough import is_env_passthrough as _is_passthrough
except Exception:
_is_passthrough = lambda _: False # noqa: E731
@ -205,7 +205,7 @@ def _make_run_env(env: dict) -> dict:
# Per-profile HOME isolation: redirect system tool configs (git, ssh, gh,
# npm …) into {HERMES_HOME}/home/ when that directory exists. Only the
# subprocess sees the override — the Python process keeps the real HOME.
from hermes_constants import get_subprocess_home
from hermes_agent.constants import get_subprocess_home
_profile_home = get_subprocess_home()
if _profile_home:
run_env["HOME"] = _profile_home
@ -220,7 +220,7 @@ def _read_terminal_shell_init_config() -> tuple[list[str], bool]:
execution never breaks because the config file is unreadable.
"""
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
cfg = load_config() or {}
terminal_cfg = cfg.get("terminal") or {}

View file

@ -10,12 +10,12 @@ import uuid
from dataclasses import dataclass
from typing import Any, Dict, Optional
from tools.environments.modal_utils import (
from hermes_agent.backends.modal_utils import (
BaseModalExecutionEnvironment,
ModalExecStart,
PreparedModalExec,
)
from tools.managed_tool_gateway import resolve_managed_tool_gateway
from hermes_agent.tools.managed_gateway import resolve_managed_tool_gateway
logger = logging.getLogger(__name__)
@ -214,7 +214,7 @@ class ManagedModalEnvironment(BaseModalExecutionEnvironment):
def _guard_unsupported_credential_passthrough(self) -> None:
"""Managed Modal does not sync or mount host credential files."""
try:
from tools.credential_files import get_credential_file_mounts
from hermes_agent.tools.credential_files import get_credential_file_mounts
except Exception:
return

View file

@ -14,14 +14,14 @@ import threading
from pathlib import Path
from typing import Any, Optional
from hermes_constants import get_hermes_home
from tools.environments.base import (
from hermes_agent.constants import get_hermes_home
from hermes_agent.backends.base import (
BaseEnvironment,
_ThreadedProcessHandle,
_load_json_store,
_save_json_store,
)
from tools.environments.file_sync import (
from hermes_agent.backends.file_sync import (
FileSyncManager,
iter_sync_files,
quoted_mkdir_command,
@ -187,7 +187,7 @@ class ModalEnvironment(BaseEnvironment):
cred_mounts = []
try:
from tools.credential_files import (
from hermes_agent.tools.credential_files import (
get_credential_file_mounts,
iter_skills_files,
iter_cache_files,

View file

@ -20,8 +20,8 @@ from abc import abstractmethod
from dataclasses import dataclass
from typing import Any
from tools.environments.base import BaseEnvironment
from tools.interrupt import is_interrupted
from hermes_agent.backends.base import BaseEnvironment
from hermes_agent.tools.interrupt import is_interrupted
@dataclass(frozen=True)
@ -136,7 +136,7 @@ class BaseModalExecutionEnvironment(BaseEnvironment):
# Periodic activity touch so the gateway knows we're alive
try:
from tools.environments.base import touch_activity_if_due
from hermes_agent.backends.base import touch_activity_if_due
touch_activity_if_due(_activity_state, "modal command running")
except Exception:
pass

View file

@ -14,8 +14,8 @@ import uuid
from pathlib import Path
from typing import Optional
from hermes_constants import get_hermes_home
from tools.environments.base import (
from hermes_agent.constants import get_hermes_home
from hermes_agent.backends.base import (
BaseEnvironment,
_load_json_store,
_popen_bash,
@ -75,7 +75,7 @@ def _get_scratch_dir() -> Path:
scratch_path.mkdir(parents=True, exist_ok=True)
return scratch_path
from tools.environments.base import get_sandbox_dir
from hermes_agent.backends.base import get_sandbox_dir
sandbox = get_sandbox_dir() / "singularity"
scratch = Path("/scratch")
@ -202,7 +202,7 @@ class SingularityEnvironment(BaseEnvironment):
cmd.append("--writable-tmpfs")
try:
from tools.credential_files import get_credential_file_mounts, get_skills_directory_mount
from hermes_agent.tools.credential_files import get_credential_file_mounts, get_skills_directory_mount
for mount_entry in get_credential_file_mounts():
cmd.extend(["--bind", f"{mount_entry['host_path']}:{mount_entry['container_path']}:ro"])
for skills_mount in get_skills_directory_mount():

View file

@ -9,8 +9,8 @@ import subprocess
import tempfile
from pathlib import Path
from tools.environments.base import BaseEnvironment, _popen_bash
from tools.environments.file_sync import (
from hermes_agent.backends.base import BaseEnvironment, _popen_bash
from hermes_agent.backends.file_sync import (
FileSyncManager,
iter_sync_files,
quoted_mkdir_command,

View file

@ -38,8 +38,8 @@ from typing import Any, Dict, List, Optional
import httpx
import yaml
from hermes_cli.config import get_hermes_home, get_config_path, read_raw_config
from hermes_constants import OPENROUTER_BASE_URL
from hermes_agent.cli.config import get_hermes_home, get_config_path, read_raw_config
from hermes_agent.constants import OPENROUTER_BASE_URL
logger = logging.getLogger(__name__)
@ -329,7 +329,7 @@ def get_anthropic_key() -> str:
ANTHROPIC_API_KEY -> ANTHROPIC_TOKEN -> CLAUDE_CODE_OAUTH_TOKEN
"""
from hermes_cli.config import get_env_value
from hermes_agent.cli.config import get_env_value
for var in PROVIDER_REGISTRY["anthropic"].api_key_env_vars:
value = get_env_value(var) or os.getenv(var, "")
@ -406,7 +406,7 @@ def _resolve_api_key_provider_secret(
if provider_id == "copilot":
# Use the dedicated copilot auth module for proper token validation
try:
from hermes_cli.copilot_auth import resolve_copilot_token
from hermes_agent.cli.auth.copilot import resolve_copilot_token
token, source = resolve_copilot_token()
if token:
return token, source
@ -866,7 +866,7 @@ def is_provider_explicitly_configured(provider_id: str) -> bool:
# 2. Check config.yaml model.provider
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
cfg = load_config()
model_cfg = cfg.get("model")
if isinstance(model_cfg, dict):
@ -953,7 +953,7 @@ def _get_config_hint_for_unknown_provider(provider_name: str) -> str:
and returns a human-readable diagnostic, or empty string if nothing found.
"""
try:
from hermes_cli.config import validate_config_structure
from hermes_agent.cli.config import validate_config_structure
issues = validate_config_structure()
if not issues:
return ""
@ -1068,7 +1068,7 @@ def resolve_provider(
# AWS Bedrock — detect via boto3 credential chain (IAM roles, SSO, env vars).
# This runs after API-key providers so explicit keys always win.
try:
from agent.bedrock_adapter import has_aws_credentials
from hermes_agent.providers.bedrock_adapter import has_aws_credentials
if has_aws_credentials():
return "bedrock"
except ImportError:
@ -1331,7 +1331,7 @@ def resolve_gemini_oauth_runtime_credentials(
) -> Dict[str, Any]:
"""Resolve runtime OAuth creds for google-gemini-cli."""
try:
from agent.google_oauth import (
from hermes_agent.providers.google_oauth import (
GoogleOAuthError,
_credentials_path,
get_valid_access_token,
@ -1370,7 +1370,7 @@ def resolve_gemini_oauth_runtime_credentials(
def get_gemini_oauth_auth_status() -> Dict[str, Any]:
"""Return a status dict for `hermes auth list` / `hermes status`."""
try:
from agent.google_oauth import _credentials_path, load_credentials
from hermes_agent.providers.google_oauth import _credentials_path, load_credentials
except ImportError:
return {"logged_in": False, "error": "agent.google_oauth unavailable"}
auth_path = _credentials_path()
@ -2159,7 +2159,7 @@ def persist_nous_credentials(
Returns the upserted :class:`PooledCredential` entry (or ``None`` if
seeding somehow produced no match shouldn't happen).
"""
from agent.credential_pool import load_pool
from hermes_agent.providers.credential_pool import load_pool
state = dict(creds)
if label and str(label).strip():
@ -2440,7 +2440,7 @@ def get_nous_auth_status() -> Dict[str, Any]:
# Check credential pool first — the dashboard device-code flow saves
# here but may not have written to the auth store yet.
try:
from agent.credential_pool import load_pool
from hermes_agent.providers.credential_pool import load_pool
pool = load_pool("nous")
if pool and pool.has_credentials():
entry = pool.select()
@ -2494,7 +2494,7 @@ def get_codex_auth_status() -> Dict[str, Any]:
# Check credential pool first — this is where `hermes auth` and
# `hermes model` store device_code tokens.
try:
from agent.credential_pool import load_pool
from hermes_agent.providers.credential_pool import load_pool
pool = load_pool("openai-codex")
if pool and pool.has_credentials():
entry = pool.select()
@ -2615,7 +2615,7 @@ def get_auth_status(provider_id: Optional[str] = None) -> Dict[str, Any]:
# AWS SDK providers (Bedrock) — check via boto3 credential chain
if pconfig and pconfig.auth_type == "aws_sdk":
try:
from agent.bedrock_adapter import has_aws_credentials
from hermes_agent.providers.bedrock_adapter import has_aws_credentials
return {"logged_in": has_aws_credentials(), "provider": target}
except ImportError:
return {"logged_in": False, "provider": target, "error": "boto3 not installed"}
@ -2804,7 +2804,7 @@ def _prompt_model_selection(
If *unavailable_models* is provided, those models are shown grayed out
and unselectable, with an upgrade link to *portal_url*.
"""
from hermes_cli.models import _format_price_per_mtok
from hermes_agent.cli.models.models import _format_price_per_mtok
_unavailable = unavailable_models or []
@ -2914,7 +2914,7 @@ def _prompt_model_selection(
title=effective_title,
)
idx = menu.show()
from hermes_cli.curses_ui import flush_stdin
from hermes_agent.cli.ui.curses import flush_stdin
flush_stdin()
if idx is None:
return None
@ -2971,7 +2971,7 @@ def _save_model_choice(model_id: str) -> None:
The model is stored in config.yaml only NOT in .env. This avoids
conflicts in multi-agent setups where env vars would stomp each other.
"""
from hermes_cli.config import save_config, load_config
from hermes_agent.cli.config import save_config, load_config
config = load_config()
# Always use dict format so provider/base_url can be stored alongside
@ -3050,7 +3050,7 @@ def _login_openai_codex(args, pconfig: ProviderConfig) -> None:
config_path = _update_config_for_provider("openai-codex", creds.get("base_url", DEFAULT_CODEX_BASE_URL))
print()
print("Login successful!")
from hermes_constants import display_hermes_home as _dhh
from hermes_agent.constants import display_hermes_home as _dhh
print(f" Auth state: {_dhh()}/auth.json")
print(f" Config updated: {config_path} (model.provider=openai-codex)")
@ -3387,7 +3387,7 @@ def _login_nous(args, pconfig: ProviderConfig) -> None:
code="invalid_token",
)
from hermes_cli.models import (
from hermes_agent.cli.models.models import (
_PROVIDER_MODELS, get_pricing_for_provider,
check_nous_free_tier, partition_nous_models_by_tier,
)

View file

@ -9,7 +9,7 @@ import time
from types import SimpleNamespace
import uuid
from agent.credential_pool import (
from hermes_agent.providers.credential_pool import (
AUTH_TYPE_API_KEY,
AUTH_TYPE_OAUTH,
CUSTOM_POOL_PREFIX,
@ -27,9 +27,9 @@ from agent.credential_pool import (
list_custom_pool_providers,
load_pool,
)
import hermes_cli.auth as auth_mod
from hermes_cli.auth import PROVIDER_REGISTRY
from hermes_constants import OPENROUTER_BASE_URL
import hermes_agent.cli.auth.auth as auth_mod
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
from hermes_agent.constants import OPENROUTER_BASE_URL
# Providers that support OAuth login in addition to API keys.
@ -39,7 +39,7 @@ _OAUTH_CAPABLE_PROVIDERS = {"anthropic", "nous", "openai-codex", "qwen-oauth", "
def _get_custom_provider_names() -> list:
"""Return list of (display_name, pool_key, provider_key) tuples."""
try:
from hermes_cli.config import get_compatible_custom_providers, load_config
from hermes_agent.cli.config import get_compatible_custom_providers, load_config
config = load_config()
except Exception:
@ -88,7 +88,7 @@ def _provider_base_url(provider: str) -> str:
if provider == "openrouter":
return OPENROUTER_BASE_URL
if provider.startswith(CUSTOM_POOL_PREFIX):
from agent.credential_pool import _get_custom_provider_config
from hermes_agent.providers.credential_pool import _get_custom_provider_config
cp_config = _get_custom_provider_config(provider)
if cp_config:
@ -159,7 +159,7 @@ def auth_add_command(args) -> None:
# Matches the Codex device_code re-link pattern that predates this.
if not provider.startswith(CUSTOM_POOL_PREFIX):
try:
from hermes_cli.auth import (
from hermes_agent.cli.auth.auth import (
_load_auth_store,
unsuppress_credential_source,
)
@ -197,7 +197,7 @@ def auth_add_command(args) -> None:
return
if provider == "anthropic":
from agent import anthropic_adapter as anthropic_mod
from hermes_agent.agent import anthropic_adapter as anthropic_mod
creds = anthropic_mod.run_hermes_oauth_login_pure()
if not creds:
@ -271,7 +271,7 @@ def auth_add_command(args) -> None:
return
if provider == "google-gemini-cli":
from agent.google_oauth import run_gemini_oauth_login_pure
from hermes_agent.providers.google_oauth import run_gemini_oauth_login_pure
creds = run_gemini_oauth_login_pure()
label = (getattr(args, "label", None) or "").strip() or (
@ -361,8 +361,8 @@ def auth_remove_command(args) -> None:
# handles its source-specific cleanup and we centralise suppression +
# user-facing output here so every source behaves identically from
# the user's perspective.
from agent.credential_sources import find_removal_step
from hermes_cli.auth import suppress_credential_source
from hermes_agent.providers.credential_sources import find_removal_step
from hermes_agent.cli.auth.auth import suppress_credential_source
step = find_removal_step(provider, removed.source)
if step is None:
@ -396,7 +396,7 @@ def _interactive_auth() -> None:
# Show AWS Bedrock credential status (not in the pool — uses boto3 chain)
try:
from agent.bedrock_adapter import has_aws_credentials, resolve_aws_auth_env_var, resolve_bedrock_region
from hermes_agent.providers.bedrock_adapter import has_aws_credentials, resolve_aws_auth_env_var, resolve_bedrock_region
if has_aws_credentials():
auth_source = resolve_aws_auth_env_var() or "unknown"
region = resolve_bedrock_region()
@ -558,7 +558,7 @@ def _interactive_strategy() -> None:
print("Invalid choice.")
return
from hermes_cli.config import load_config, save_config
from hermes_agent.cli.config import load_config, save_config
cfg = load_config()
pool_strategies = cfg.get("credential_pool_strategies") or {}
if not isinstance(pool_strategies, dict):

View file

@ -234,7 +234,7 @@ def dingtalk_qr_auth() -> Optional[Tuple[str, str]]:
Returns (client_id, client_secret) on success, or None if the user
cancelled or the flow failed.
"""
from hermes_cli.setup import print_info, print_success, print_warning, print_error
from hermes_agent.cli.setup_wizard import print_info, print_success, print_warning, print_error
print()
print_info(" Initializing DingTalk device authorization...")

View file

@ -21,7 +21,7 @@ from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, List, Optional
from hermes_constants import get_default_hermes_root, get_hermes_home, display_hermes_home
from hermes_agent.constants import get_default_hermes_root, get_hermes_home, display_hermes_home
logger = logging.getLogger(__name__)
@ -396,7 +396,7 @@ def run_import(args) -> None:
restored_profiles = []
if profiles_dir.is_dir():
try:
from hermes_cli.profiles import (
from hermes_agent.cli.profiles import (
create_wrapper_script, check_alias_collision,
_is_wrapper_dir_in_path, _get_wrapper_dir,
)

View file

@ -16,9 +16,9 @@ import sys
from datetime import datetime
from pathlib import Path
from hermes_cli.config import get_hermes_home, get_config_path, load_config, save_config
from hermes_constants import get_optional_skills_dir
from hermes_cli.setup import (
from hermes_agent.cli.config import get_hermes_home, get_config_path, load_config, save_config
from hermes_agent.constants import get_optional_skills_dir
from hermes_agent.cli.setup_wizard import (
Colors,
color,
print_header,
@ -153,7 +153,7 @@ def _warn_if_gateway_running(auto_yes: bool) -> None:
(e.g. Telegram 409 "terminated by other getUpdates request"). Warn the
user and let them decide whether to continue.
"""
from gateway.status import get_running_pid, read_runtime_status
from hermes_agent.gateway.status import get_running_pid, read_runtime_status
if not get_running_pid():
return

View file

@ -19,7 +19,7 @@ import subprocess
import sys
from pathlib import Path
from hermes_constants import is_wsl as _is_wsl
from hermes_agent.constants import is_wsl as _is_wsl
logger = logging.getLogger(__name__)

View file

@ -131,7 +131,7 @@ COMMAND_REGISTRY: list[CommandDef] = [
# Tools & Skills
CommandDef("tools", "Manage tools: /tools [list|disable|enable] [name...]", "Tools & Skills",
args_hint="[list|disable|enable] [name...]", cli_only=True),
CommandDef("toolsets", "List available toolsets", "Tools & Skills",
CommandDef("hermes_agent.tools.toolsets", "List available toolsets", "Tools & Skills",
cli_only=True),
CommandDef("skills", "Search, install, inspect, or manage skills",
"Tools & Skills", cli_only=True,
@ -318,7 +318,7 @@ def _resolve_config_gates() -> set[str]:
if not gated:
return set()
try:
from hermes_cli.config import read_raw_config
from hermes_agent.cli.config import read_raw_config
cfg = read_raw_config()
except Exception:
return set()
@ -497,7 +497,7 @@ def _collect_gateway_skill_entries(
# --- Tier 1: Plugin slash commands (never trimmed) ---------------------
plugin_pairs: list[tuple[str, str]] = []
try:
from hermes_cli.plugins import get_plugin_commands
from hermes_agent.cli.plugins import get_plugin_commands
plugin_cmds = get_plugin_commands()
for cmd_name in sorted(plugin_cmds):
name = sanitize_name(cmd_name) if sanitize_name else cmd_name
@ -519,15 +519,15 @@ def _collect_gateway_skill_entries(
# --- Tier 2: Built-in skill commands (trimmed at cap) -----------------
_platform_disabled: set[str] = set()
try:
from agent.skill_utils import get_disabled_skill_names
from hermes_agent.agent.skill_utils import get_disabled_skill_names
_platform_disabled = get_disabled_skill_names(platform=platform)
except Exception:
pass
skill_triples: list[tuple[str, str, str]] = []
try:
from agent.skill_commands import get_skill_commands
from tools.skills_tool import SKILLS_DIR
from hermes_agent.agent.skill_commands import get_skill_commands
from hermes_agent.tools.skills.tool import SKILLS_DIR
_skills_dir = str(SKILLS_DIR.resolve())
_hub_dir = str((SKILLS_DIR / ".hub").resolve())
skill_cmds = get_skill_commands()
@ -661,7 +661,7 @@ def discord_skill_commands_by_category(
_platform_disabled: set[str] = set()
try:
from agent.skill_utils import get_disabled_skill_names
from hermes_agent.agent.skill_utils import get_disabled_skill_names
_platform_disabled = get_disabled_skill_names(platform="discord")
except Exception:
pass
@ -673,8 +673,8 @@ def discord_skill_commands_by_category(
hidden = 0
try:
from agent.skill_commands import get_skill_commands
from tools.skills_tool import SKILLS_DIR
from hermes_agent.agent.skill_commands import get_skill_commands
from hermes_agent.tools.skills.tool import SKILLS_DIR
_skills_dir = SKILLS_DIR.resolve()
_hub_dir = (SKILLS_DIR / ".hub").resolve()
skill_cmds = get_skill_commands()
@ -1116,7 +1116,7 @@ class SlashCommandCompleter(Completer):
def _skin_completions(sub_text: str, sub_lower: str):
"""Yield completions for /skin from available skins."""
try:
from hermes_cli.skin_engine import list_skins
from hermes_agent.cli.ui.skin_engine import list_skins
for s in list_skins():
name = s["name"]
if name.startswith(sub_lower) and name != sub_lower:
@ -1133,7 +1133,7 @@ class SlashCommandCompleter(Completer):
def _personality_completions(sub_text: str, sub_lower: str):
"""Yield completions for /personality from configured personalities."""
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
personalities = load_config().get("agent", {}).get("personalities", {})
if "none".startswith(sub_lower) and "none" != sub_lower:
yield Completion(
@ -1162,7 +1162,7 @@ class SlashCommandCompleter(Completer):
seen = set()
# Config-based direct aliases (preferred — include provider info)
try:
from hermes_cli.model_switch import (
from hermes_agent.cli.models.switch import (
_ensure_direct_aliases, DIRECT_ALIASES, MODEL_ALIASES,
)
_ensure_direct_aliases()
@ -1262,7 +1262,7 @@ class SlashCommandCompleter(Completer):
# Plugin-registered slash commands
try:
from hermes_cli.plugins import get_plugin_commands
from hermes_agent.cli.plugins import get_plugin_commands
for cmd_name, cmd_info in get_plugin_commands().items():
if cmd_name.startswith(word):
desc = str(cmd_info.get("description", "Plugin command"))

View file

@ -61,8 +61,8 @@ _EXTRA_ENV_KEYS = frozenset({
})
import yaml
from hermes_cli.colors import Colors, color
from hermes_cli.default_soul import DEFAULT_SOUL_MD
from hermes_agent.cli.ui.colors import Colors, color
from hermes_agent.cli.default_soul import DEFAULT_SOUL_MD
# =============================================================================
@ -169,7 +169,7 @@ def get_container_exec_info() -> Optional[dict]:
if os.environ.get("HERMES_DEV") == "1":
return None
from hermes_constants import is_container
from hermes_agent.constants import is_container
if is_container():
return None
@ -205,7 +205,7 @@ def get_container_exec_info() -> Optional[dict]:
# =============================================================================
# Re-export from hermes_constants — canonical definition lives there.
from hermes_constants import get_hermes_home # noqa: F811,E402
from hermes_agent.constants import get_hermes_home # noqa: F811,E402
def get_config_path() -> Path:
"""Get the main config file path."""
@ -699,7 +699,7 @@ DEFAULT_CONFIG: _DefaultConfig = {
"providers": {},
"fallback_providers": [],
"credential_pool_strategies": {},
"toolsets": ["hermes-cli"],
"hermes_agent.tools.toolsets": ["hermes-cli"],
"agent": {
"max_turns": 90,
# Inactivity timeout for gateway agent execution (seconds).
@ -2230,7 +2230,7 @@ def get_missing_skill_config_vars() -> List[Dict[str, Any]]:
config.yaml. Returns a list of dicts suitable for prompting.
"""
try:
from agent.skill_utils import discover_all_skill_config_vars, SKILL_CONFIG_PREFIX
from hermes_agent.agent.skill_utils import discover_all_skill_config_vars, SKILL_CONFIG_PREFIX
except Exception:
return []
@ -2450,7 +2450,7 @@ def check_config_version() -> Tuple[int, int]:
# Fields that are valid at root level of config.yaml
_KNOWN_ROOT_KEYS = {
"_config_version", "model", "providers", "fallback_model",
"fallback_providers", "credential_pool_strategies", "toolsets",
"fallback_providers", "credential_pool_strategies", "hermes_agent.tools.toolsets",
"agent", "terminal", "display", "compression", "delegation",
"auxiliary", "custom_providers", "context", "memory", "gateway",
}
@ -3132,7 +3132,7 @@ def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, A
print()
config = load_config()
try:
from agent.skill_utils import SKILL_CONFIG_PREFIX
from hermes_agent.agent.skill_utils import SKILL_CONFIG_PREFIX
except Exception:
SKILL_CONFIG_PREFIX = "skills.config"
for var in missing_skill_config:
@ -3447,7 +3447,7 @@ def save_config(config: Dict[str, Any]):
if is_managed():
managed_error("save configuration")
return
from utils import atomic_yaml_write
from hermes_agent.utils import atomic_yaml_write
ensure_hermes_home()
config_path = get_config_path()
@ -3883,7 +3883,7 @@ def show_config():
for env_key, name in keys:
value = get_env_value(env_key)
print(f" {name:<14} {redact_key(value)}")
from hermes_cli.auth import get_anthropic_key
from hermes_agent.cli.auth.auth import get_anthropic_key
anthropic_value = get_anthropic_key()
print(f" {'Anthropic':<14} {redact_key(anthropic_value)}")
@ -3991,7 +3991,7 @@ def show_config():
# Skill config
try:
from agent.skill_utils import discover_all_skill_config_vars, resolve_skill_config_values
from hermes_agent.agent.skill_utils import discover_all_skill_config_vars, resolve_skill_config_values
skill_vars = discover_all_skill_config_vars()
if skill_vars:
resolved = resolve_skill_config_values(skill_vars)
@ -4105,7 +4105,7 @@ def set_config_value(key: str, value: str):
# Write only user config back (not the full merged defaults)
ensure_hermes_home()
from utils import atomic_yaml_write
from hermes_agent.utils import atomic_yaml_write
atomic_yaml_write(config_path, user_config, sort_keys=False)
# Keep .env in sync for keys that terminal_tool reads directly from env vars.

View file

@ -11,9 +11,8 @@ from pathlib import Path
from typing import Iterable, List, Optional
PROJECT_ROOT = Path(__file__).parent.parent.resolve()
sys.path.insert(0, str(PROJECT_ROOT))
from hermes_cli.colors import Colors, color
from hermes_agent.cli.ui.colors import Colors, color
def _normalize_skills(single_skill=None, skills: Optional[Iterable[str]] = None) -> Optional[List[str]]:
@ -33,14 +32,14 @@ def _normalize_skills(single_skill=None, skills: Optional[Iterable[str]] = None)
def _cron_api(**kwargs):
from tools.cronjob_tools import cronjob as cronjob_tool
from hermes_agent.tools.cronjob import cronjob as cronjob_tool
return json.loads(cronjob_tool(**kwargs))
def cron_list(show_all: bool = False):
"""List all scheduled jobs."""
from cron.jobs import list_jobs
from hermes_agent.cron.jobs import list_jobs
jobs = list_jobs(include_disabled=show_all)
@ -110,7 +109,7 @@ def cron_list(show_all: bool = False):
print()
from hermes_cli.gateway import find_gateway_pids
from hermes_agent.cli.gateway import find_gateway_pids
if not find_gateway_pids():
print(color(" ⚠ Gateway is not running — jobs won't fire automatically.", Colors.YELLOW))
print(color(" Start it with: hermes gateway install", Colors.DIM))
@ -120,14 +119,14 @@ def cron_list(show_all: bool = False):
def cron_tick():
"""Run due jobs once and exit."""
from cron.scheduler import tick
from hermes_agent.cron.scheduler import tick
tick(verbose=True)
def cron_status():
"""Show cron execution status."""
from cron.jobs import list_jobs
from hermes_cli.gateway import find_gateway_pids
from hermes_agent.cron.jobs import list_jobs
from hermes_agent.cli.gateway import find_gateway_pids
print()
@ -185,7 +184,7 @@ def cron_create(args):
def cron_edit(args):
from cron.jobs import get_job
from hermes_agent.cron.jobs import get_job
job = get_job(args.job_id)
if not job:

View file

@ -16,7 +16,7 @@ import urllib.request
from pathlib import Path
from typing import Optional
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
# ---------------------------------------------------------------------------
@ -319,7 +319,7 @@ def _resolve_log_path(log_name: str) -> Optional[Path]:
Returns the path if found, or None.
"""
from hermes_cli.logs import LOG_FILES
from hermes_agent.cli.logs import LOG_FILES
filename = LOG_FILES.get(log_name)
if not filename:
@ -340,7 +340,7 @@ def _resolve_log_path(log_name: str) -> Optional[Path]:
def _read_log_tail(log_name: str, num_lines: int) -> str:
"""Read the last *num_lines* from a log file, or return a placeholder."""
from hermes_cli.logs import _read_last_n_lines
from hermes_agent.cli.logs import _read_last_n_lines
log_path = _resolve_log_path(log_name)
if log_path is None:
@ -388,7 +388,7 @@ def _read_full_log(log_name: str, max_bytes: int = _MAX_LOG_BYTES) -> Optional[s
def _capture_dump() -> str:
"""Run ``hermes dump`` and return its stdout as a string."""
from hermes_cli.dump import run_dump
from hermes_agent.cli.dump import run_dump
class _FakeArgs:
show_keys = False

View file

@ -10,8 +10,8 @@ import subprocess
import shutil
from pathlib import Path
from hermes_cli.config import get_project_root, get_hermes_home, get_env_path
from hermes_constants import display_hermes_home
from hermes_agent.cli.config import get_project_root, get_hermes_home, get_env_path
from hermes_agent.constants import display_hermes_home
PROJECT_ROOT = get_project_root()
HERMES_HOME = get_hermes_home()
@ -28,9 +28,9 @@ if _env_path.exists():
# Also try project .env as dev fallback
load_dotenv(PROJECT_ROOT / ".env", override=False, encoding="utf-8")
from hermes_cli.colors import Colors, color
from hermes_constants import OPENROUTER_MODELS_URL
from utils import base_url_host_matches
from hermes_agent.cli.ui.colors import Colors, color
from hermes_agent.constants import OPENROUTER_MODELS_URL
from hermes_agent.utils import base_url_host_matches
_PROVIDER_ENV_HINTS = (
@ -58,7 +58,7 @@ _PROVIDER_ENV_HINTS = (
)
from hermes_constants import is_termux as _is_termux
from hermes_agent.constants import is_termux as _is_termux
def _python_install_cmd() -> str:
@ -92,7 +92,7 @@ def _has_provider_env_config(content: str) -> bool:
def _honcho_is_configured_for_doctor() -> bool:
"""Return True when Honcho is configured, even if this process has no active session."""
try:
from plugins.memory.honcho.client import HonchoClientConfig
from hermes_agent.plugins.memory.honcho.client import HonchoClientConfig
cfg = HonchoClientConfig.from_global_config()
return bool(cfg.enabled and (cfg.api_key or cfg.base_url))
@ -132,7 +132,7 @@ def check_info(text: str):
def _check_gateway_service_linger(issues: list[str]) -> None:
"""Warn when a systemd user gateway service will stop after logout."""
try:
from hermes_cli.gateway import (
from hermes_agent.cli.gateway import (
get_systemd_linger_status,
get_systemd_unit_path,
is_linux,
@ -290,12 +290,12 @@ def run_doctor(args):
known_providers: set = set()
try:
from hermes_cli.auth import PROVIDER_REGISTRY
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
known_providers = set(PROVIDER_REGISTRY.keys()) | {"openrouter", "custom", "auto"}
except Exception:
pass
try:
from hermes_cli.auth import resolve_provider as _resolve_provider
from hermes_agent.cli.auth.auth import resolve_provider as _resolve_provider
except Exception:
_resolve_provider = None
@ -338,7 +338,7 @@ def run_doctor(args):
# explicitly dispatch, which would produce false positives.
if canonical_provider and canonical_provider not in ("auto", "custom", "openrouter"):
try:
from hermes_cli.auth import PROVIDER_REGISTRY, get_auth_status
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY, get_auth_status
pconfig = PROVIDER_REGISTRY.get(canonical_provider)
if pconfig and getattr(pconfig, "auth_type", "") == "api_key":
status = get_auth_status(canonical_provider) or {}
@ -379,7 +379,7 @@ def run_doctor(args):
config_path = HERMES_HOME / 'config.yaml'
if config_path.exists():
try:
from hermes_cli.config import check_config_version, migrate_config
from hermes_agent.cli.config import check_config_version, migrate_config
current_ver, latest_ver = check_config_version()
if current_ver < latest_ver:
check_warn(
@ -419,7 +419,7 @@ def run_doctor(args):
model_section[k] = raw_config.pop(k)
else:
raw_config.pop(k)
from utils import atomic_yaml_write
from hermes_agent.utils import atomic_yaml_write
atomic_yaml_write(config_path, raw_config)
check_ok("Migrated stale root-level keys into model section")
fixed_count += 1
@ -430,7 +430,7 @@ def run_doctor(args):
# Validate config structure (catches malformed custom_providers, etc.)
try:
from hermes_cli.config import validate_config_structure
from hermes_agent.cli.config import validate_config_structure
config_issues = validate_config_structure()
if config_issues:
print()
@ -454,7 +454,7 @@ def run_doctor(args):
print(color("◆ Auth Providers", Colors.CYAN, Colors.BOLD))
try:
from hermes_cli.auth import (
from hermes_agent.cli.auth.auth import (
get_nous_auth_status,
get_codex_auth_status,
get_gemini_oauth_auth_status,
@ -877,13 +877,13 @@ def run_doctor(args):
else:
check_warn("OpenRouter API", "(not configured)")
from hermes_cli.auth import get_anthropic_key
from hermes_agent.cli.auth.auth import get_anthropic_key
anthropic_key = get_anthropic_key()
if anthropic_key:
print(" Checking Anthropic API...", end="", flush=True)
try:
import httpx
from agent.anthropic_adapter import _is_oauth_token, _COMMON_BETAS, _OAUTH_ONLY_BETAS
from hermes_agent.providers.anthropic_adapter import _is_oauth_token, _COMMON_BETAS, _OAUTH_ONLY_BETAS
headers = {"anthropic-version": "2023-06-01"}
if _is_oauth_token(anthropic_key):
@ -951,7 +951,7 @@ def run_doctor(args):
# with no /v1) don't support /models. Rewrite to the OpenAI-compat
# /v1 surface for health checks.
if _base and _base.rstrip("/").endswith("/anthropic"):
from agent.auxiliary_client import _to_openai_base_url
from hermes_agent.providers.auxiliary import _to_openai_base_url
_base = _to_openai_base_url(_base)
if base_url_host_matches(_base, "api.kimi.com") and _base.rstrip("/").endswith("/coding"):
_base = _base.rstrip("/") + "/v1"
@ -977,7 +977,7 @@ def run_doctor(args):
# -- AWS Bedrock --
# Bedrock uses the AWS SDK credential chain, not API keys.
try:
from agent.bedrock_adapter import has_aws_credentials, resolve_aws_auth_env_var, resolve_bedrock_region
from hermes_agent.providers.bedrock_adapter import has_aws_credentials, resolve_aws_auth_env_var, resolve_bedrock_region
if has_aws_credentials():
_auth_var = resolve_aws_auth_env_var()
_region = resolve_bedrock_region()
@ -1028,9 +1028,7 @@ def run_doctor(args):
print(color("◆ Tool Availability", Colors.CYAN, Colors.BOLD))
try:
# Add project root to path for imports
sys.path.insert(0, str(PROJECT_ROOT))
from model_tools import check_tool_availability, TOOLSET_REQUIREMENTS
from hermes_agent.tools.dispatch import check_tool_availability, TOOLSET_REQUIREMENTS
available, unavailable = check_tool_availability()
available, unavailable = _apply_doctor_tool_availability_overrides(available, unavailable)
@ -1079,7 +1077,7 @@ def run_doctor(args):
else:
check_warn("Skills Hub directory not initialized", "(run: hermes skills list)")
from hermes_cli.config import get_env_value
from hermes_agent.cli.config import get_env_value
github_token = get_env_value("GITHUB_TOKEN") or get_env_value("GH_TOKEN")
if github_token:
check_ok("GitHub token configured (authenticated API access)")
@ -1107,7 +1105,7 @@ def run_doctor(args):
check_ok("Built-in memory active", "(no external provider configured — this is fine)")
elif _active_memory_provider == "honcho":
try:
from plugins.memory.honcho.client import HonchoClientConfig, resolve_config_path
from hermes_agent.plugins.memory.honcho.client import HonchoClientConfig, resolve_config_path
hcfg = HonchoClientConfig.from_global_config()
_honcho_cfg_path = resolve_config_path()
@ -1119,7 +1117,7 @@ def run_doctor(args):
check_fail("Honcho API key or base URL not set", "run: hermes memory setup")
issues.append("No Honcho API key — run 'hermes memory setup'")
else:
from plugins.memory.honcho.client import get_honcho_client, reset_honcho_client
from hermes_agent.plugins.memory.honcho.client import get_honcho_client, reset_honcho_client
reset_honcho_client()
try:
get_honcho_client(hcfg)
@ -1137,7 +1135,7 @@ def run_doctor(args):
check_warn("Honcho check failed", str(_e))
elif _active_memory_provider == "mem0":
try:
from plugins.memory.mem0 import _load_config as _load_mem0_config
from hermes_agent.plugins.memory.mem0 import _load_config as _load_mem0_config
mem0_cfg = _load_mem0_config()
mem0_key = mem0_cfg.get("api_key", "")
if mem0_key:
@ -1154,7 +1152,7 @@ def run_doctor(args):
else:
# Generic check for other memory providers (openviking, hindsight, etc.)
try:
from plugins.memory import load_memory_provider
from hermes_agent.plugins.memory import load_memory_provider
_provider = load_memory_provider(_active_memory_provider)
if _provider and _provider.is_available():
check_ok(f"{_active_memory_provider} provider active")
@ -1169,7 +1167,7 @@ def run_doctor(args):
# Profiles
# =========================================================================
try:
from hermes_cli.profiles import list_profiles, _get_wrapper_dir, profile_exists
from hermes_agent.cli.profiles import list_profiles, _get_wrapper_dir, profile_exists
import re as _re
named_profiles = [p for p in list_profiles() if not p.is_default]

View file

@ -13,8 +13,8 @@ import subprocess
import sys
from pathlib import Path
from hermes_cli.config import get_hermes_home, get_env_path, get_project_root, load_config
from hermes_constants import display_hermes_home
from hermes_agent.cli.config import get_hermes_home, get_env_path, get_project_root, load_config
from hermes_agent.constants import display_hermes_home
def _get_git_commit(project_root: Path) -> str:
@ -44,7 +44,7 @@ def _redact(value: str) -> str:
def _gateway_status() -> str:
"""Return a short gateway status string."""
try:
from hermes_cli.gateway import get_gateway_runtime_snapshot
from hermes_agent.cli.gateway import get_gateway_runtime_snapshot
snapshot = get_gateway_runtime_snapshot()
if snapshot.running:
@ -142,7 +142,7 @@ def _config_overrides(config: dict) -> dict[str, str]:
Returns a flat dict of dotpath -> value for interesting overrides.
"""
from hermes_cli.config import DEFAULT_CONFIG
from hermes_agent.cli.config import DEFAULT_CONFIG
overrides = {}
@ -178,7 +178,7 @@ def _config_overrides(config: dict) -> dict[str, str]:
default_toolsets = DEFAULT_CONFIG.get("toolsets", [])
user_toolsets = config.get("toolsets", [])
if user_toolsets != default_toolsets:
overrides["toolsets"] = str(user_toolsets)
overrides["hermes_agent.tools.toolsets"] = str(user_toolsets)
# Fallback providers
fallbacks = config.get("fallback_providers", [])
@ -207,7 +207,7 @@ def run_dump(args):
hermes_home = get_hermes_home()
try:
from hermes_cli import __version__, __release_date__
from hermes_agent.cli import __version__, __release_date__
except ImportError:
__version__ = "(unknown)"
__release_date__ = ""
@ -223,7 +223,7 @@ def run_dump(args):
# Profile
try:
from hermes_cli.profiles import get_active_profile_name
from hermes_agent.cli.profiles import get_active_profile_name
profile = get_active_profile_name() or "(default)"
except Exception:
profile = "(default)"

View file

@ -108,7 +108,7 @@ def _sanitize_env_file_if_needed(path: Path) -> None:
if not path.exists():
return
try:
from hermes_cli.config import _sanitize_env_lines
from hermes_agent.cli.config import _sanitize_env_lines
except ImportError:
return # early bootstrap — config module not available yet

View file

@ -15,13 +15,13 @@ from pathlib import Path
PROJECT_ROOT = Path(__file__).parent.parent.resolve()
from gateway.status import terminate_pid
from gateway.restart import (
from hermes_agent.gateway.status import terminate_pid
from hermes_agent.gateway.restart import (
DEFAULT_GATEWAY_RESTART_DRAIN_TIMEOUT,
GATEWAY_SERVICE_RESTART_EXIT_CODE,
parse_restart_drain_timeout,
)
from hermes_cli.config import (
from hermes_agent.cli.config import (
get_env_value,
get_hermes_home,
is_managed,
@ -31,11 +31,11 @@ from hermes_cli.config import (
)
# display_hermes_home is imported lazily at call sites to avoid ImportError
# when hermes_constants is cached from a pre-update version during `hermes update`.
from hermes_cli.setup import (
from hermes_agent.cli.setup_wizard import (
print_header, print_info, print_success, print_warning, print_error,
prompt, prompt_choice, prompt_yes_no,
)
from hermes_cli.colors import Colors, color
from hermes_agent.cli.ui.colors import Colors, color
# =============================================================================
@ -192,6 +192,12 @@ def _scan_gateway_pids(exclude_pids: set[int], all_profiles: bool = False) -> li
"""
pids: list[int] = []
patterns = [
"hermes_agent.cli.main gateway",
"hermes_agent.cli.main --profile",
"hermes_agent.cli.main -p",
"hermes_agent/cli/main.py gateway",
"hermes_agent/cli/main.py --profile",
"hermes_agent/cli/main.py -p",
"hermes_cli.main gateway",
"hermes_cli.main --profile",
"hermes_cli.main -p",
@ -303,7 +309,7 @@ def find_gateway_pids(exclude_pids: set | None = None, all_profiles: bool = Fals
pids: list[int] = []
if not all_profiles:
try:
from gateway.status import get_running_pid
from hermes_agent.gateway.status import get_running_pid
_append_unique_pid(pids, get_running_pid(), _exclude)
except Exception:
@ -357,7 +363,7 @@ def get_gateway_runtime_snapshot(system: bool = False) -> GatewayRuntimeSnapshot
gateway_pids=gateway_pids,
)
from hermes_constants import is_container
from hermes_agent.constants import is_container
if is_linux() and is_container():
return GatewayRuntimeSnapshot(
@ -445,7 +451,7 @@ def stop_profile_gateway() -> bool:
Returns True if a process was stopped, False if none was found.
"""
try:
from gateway.status import get_running_pid, remove_pid_file
from hermes_agent.gateway.status import get_running_pid, remove_pid_file
except ImportError:
return False
@ -478,7 +484,7 @@ def is_linux() -> bool:
return sys.platform.startswith('linux')
from hermes_constants import is_container, is_termux, is_wsl
from hermes_agent.constants import is_container, is_termux, is_wsl
def _wsl_systemd_operational() -> bool:
@ -552,7 +558,7 @@ def _profile_suffix() -> str:
"""
import hashlib
import re
from hermes_constants import get_default_hermes_root
from hermes_agent.constants import get_default_hermes_root
home = get_hermes_home().resolve()
default = get_default_hermes_root().resolve()
if home == default:
@ -582,7 +588,7 @@ def _profile_arg(hermes_home: str | None = None) -> str:
service definition for a different user (e.g. system service).
"""
import re
from hermes_constants import get_default_hermes_root
from hermes_agent.constants import get_default_hermes_root
home = Path(hermes_home or str(get_hermes_home())).resolve()
default = get_default_hermes_root().resolve()
if home == default:
@ -696,6 +702,8 @@ _LEGACY_SERVICE_NAMES: tuple[str, ...] = ("hermes.service",)
# ExecStart content markers that identify a unit as running our gateway.
# A legacy unit is only flagged when its file contains one of these.
_LEGACY_UNIT_EXECSTART_MARKERS: tuple[str, ...] = (
"hermes_agent.cli.main gateway",
"hermes_agent/cli/main.py gateway",
"hermes_cli.main gateway",
"hermes_cli/main.py gateway",
"gateway/run.py",
@ -1221,7 +1229,7 @@ StartLimitBurst=5
Type=simple
User={username}
Group={group_name}
ExecStart={python_path} -m hermes_cli.main{f" {profile_arg}" if profile_arg else ""} gateway run --replace
ExecStart={python_path} -m hermes_agent.cli.main{f" {profile_arg}" if profile_arg else ""} gateway run --replace
WorkingDirectory={working_dir}
Environment="HOME={home_dir}"
Environment="USER={username}"
@ -1256,7 +1264,7 @@ StartLimitBurst=5
[Service]
Type=simple
ExecStart={python_path} -m hermes_cli.main{f" {profile_arg}" if profile_arg else ""} gateway run --replace
ExecStart={python_path} -m hermes_agent.cli.main{f" {profile_arg}" if profile_arg else ""} gateway run --replace
WorkingDirectory={working_dir}
Environment="PATH={sane_path}"
Environment="VIRTUAL_ENV={venv_dir}"
@ -1501,7 +1509,7 @@ def systemd_restart(system: bool = False):
if system:
_require_root_for_system_service("restart")
refresh_systemd_unit_if_needed(system=system)
from gateway.status import get_running_pid
from hermes_agent.gateway.status import get_running_pid
pid = get_running_pid()
if pid is not None and _request_gateway_self_restart(pid):
@ -1689,7 +1697,7 @@ def generate_launchd_plist() -> str:
prog_args = [
f"<string>{python_path}</string>",
"<string>-m</string>",
"<string>hermes_cli.main</string>",
"<string>hermes_agent.cli.main</string>",
]
if profile_arg:
for part in profile_arg.split():
@ -1799,7 +1807,7 @@ def launchd_install(force: bool = False):
print()
print("Next steps:")
print(" hermes gateway status # Check status")
from hermes_constants import display_hermes_home as _dhh
from hermes_agent.constants import display_hermes_home as _dhh
print(f" tail -f {_dhh()}/logs/gateway.log # View logs")
def launchd_uninstall():
@ -1867,7 +1875,7 @@ def _wait_for_gateway_exit(timeout: float = 10.0, force_after: float | None = 5.
force_after: Seconds of graceful waiting before escalating to force-kill.
"""
import time
from gateway.status import get_running_pid
from hermes_agent.gateway.status import get_running_pid
deadline = time.monotonic() + timeout
force_deadline = (time.monotonic() + force_after) if force_after is not None else None
@ -1901,7 +1909,7 @@ def launchd_restart():
label = get_launchd_label()
target = f"{_launchd_domain()}/{label}"
drain_timeout = _get_restart_drain_timeout()
from gateway.status import get_running_pid
from hermes_agent.gateway.status import get_running_pid
try:
pid = get_running_pid()
@ -1982,9 +1990,7 @@ def run_gateway(verbose: int = 0, quiet: bool = False, replace: bool = False):
This prevents systemd restart loops when the old process
hasn't fully exited yet.
"""
sys.path.insert(0, str(PROJECT_ROOT))
from gateway.run import start_gateway
from hermes_agent.gateway.run import start_gateway
print("┌─────────────────────────────────────────────────────────┐")
print("│ ⚕ Hermes Gateway Starting... │")
@ -2430,7 +2436,7 @@ def _platform_status(platform: dict) -> str:
def _runtime_health_lines() -> list[str]:
"""Summarize the latest persisted gateway runtime health state."""
try:
from gateway.status import read_runtime_status
from hermes_agent.gateway.status import read_runtime_status
except Exception:
return []
@ -2562,7 +2568,7 @@ def _setup_standard_platform(platform: dict):
def _setup_whatsapp():
"""Delegate to the existing WhatsApp setup flow."""
from hermes_cli.main import cmd_whatsapp
from hermes_agent.cli.main import cmd_whatsapp
import argparse
cmd_whatsapp(argparse.Namespace())
@ -2581,7 +2587,7 @@ def _setup_sms():
def _setup_dingtalk():
"""Configure DingTalk — QR scan (recommended) or manual credential entry."""
from hermes_cli.setup import (
from hermes_agent.cli.setup_wizard import (
prompt_choice, prompt_yes_no, print_info, print_success, print_warning,
)
@ -2612,7 +2618,7 @@ def _setup_dingtalk():
if method == 0:
# ── QR-code device-flow authorization ──
try:
from hermes_cli.dingtalk_auth import dingtalk_qr_auth
from hermes_agent.cli.auth.dingtalk import dingtalk_qr_auth
except ImportError as exc:
print_warning(f" QR auth module failed to load ({exc}), falling back to manual input.")
_setup_standard_platform(dingtalk_platform)
@ -2720,7 +2726,7 @@ def _setup_weixin():
return
try:
from gateway.platforms.weixin import check_weixin_requirements, qr_login
from hermes_agent.gateway.platforms.weixin import check_weixin_requirements, qr_login
except Exception as exc:
print_error(f" Weixin adapter import failed: {exc}")
print_info(" Install gateway dependencies first, then retry.")
@ -2855,7 +2861,7 @@ def _setup_feishu():
if method_idx == 0:
# ── QR scan-to-create ──
try:
from gateway.platforms.feishu import qr_register
from hermes_agent.gateway.platforms.feishu import qr_register
except Exception as exc:
print_error(f" Feishu / Lark onboard import failed: {exc}")
qr_register = None
@ -2896,7 +2902,7 @@ def _setup_feishu():
# Try to probe the bot with manual credentials
bot_name = None
try:
from gateway.platforms.feishu import probe_bot
from hermes_agent.gateway.platforms.feishu import probe_bot
bot_info = probe_bot(app_id, app_secret, domain)
if bot_info:
bot_name = bot_info.get("bot_name")
@ -3129,11 +3135,11 @@ def _qqbot_qr_flow():
or None on failure/cancel.
"""
try:
from gateway.platforms.qqbot import (
from hermes_agent.gateway.platforms.qqbot import (
create_bind_task, poll_bind_result, build_connect_url,
decrypt_secret, BindStatus,
)
from gateway.platforms.qqbot.constants import ONBOARD_POLL_INTERVAL
from hermes_agent.gateway.platforms.qqbot.constants import ONBOARD_POLL_INTERVAL
except Exception as exc:
print_error(f" QQBot onboard import failed: {exc}")
return None
@ -3471,7 +3477,7 @@ def gateway_setup():
print_info(" To enable systemd: add systemd=true to /etc/wsl.conf, then 'wsl --shutdown'")
else:
if is_termux():
from hermes_constants import display_hermes_home as _dhh
from hermes_agent.constants import display_hermes_home as _dhh
print_info(" Termux does not use systemd/launchd services.")
print_info(" Run in foreground: hermes gateway run")
print_info(f" Or start it manually in the background (best effort): nohup hermes gateway run >{_dhh()}/logs/gateway.log 2>&1 &")

View file

@ -50,8 +50,8 @@ def hooks_command(args) -> None:
# ---------------------------------------------------------------------------
def _cmd_list(_args) -> None:
from hermes_cli.config import load_config
from agent import shell_hooks
from hermes_agent.cli.config import load_config
from hermes_agent.agent import shell_hooks
specs = shell_hooks.iter_configured_hooks(load_config())
@ -186,9 +186,9 @@ _DEFAULT_PAYLOADS = {
def _cmd_test(args) -> None:
from hermes_cli.config import load_config
from hermes_cli.plugins import VALID_HOOKS
from agent import shell_hooks
from hermes_agent.cli.config import load_config
from hermes_agent.cli.plugins import VALID_HOOKS
from hermes_agent.agent import shell_hooks
event = args.event
if event not in VALID_HOOKS:
@ -273,7 +273,7 @@ def _truncate(s: str, n: int) -> str:
# ---------------------------------------------------------------------------
def _cmd_revoke(args) -> None:
from agent import shell_hooks
from hermes_agent.agent import shell_hooks
removed = shell_hooks.revoke(args.command)
if removed == 0:
@ -291,8 +291,8 @@ def _cmd_revoke(args) -> None:
# ---------------------------------------------------------------------------
def _cmd_doctor(_args) -> None:
from hermes_cli.config import load_config
from agent import shell_hooks
from hermes_agent.cli.config import load_config
from hermes_agent.agent import shell_hooks
specs = shell_hooks.iter_configured_hooks(load_config())

View file

@ -24,7 +24,7 @@ from datetime import datetime, timedelta
from pathlib import Path
from typing import Optional, Sequence
from hermes_constants import get_hermes_home, display_hermes_home
from hermes_agent.constants import get_hermes_home, display_hermes_home
# Known log files (name → filename)
LOG_FILES = {
@ -191,7 +191,7 @@ def tail_log(
# Resolve component to logger name prefixes
component_prefixes = None
if component:
from hermes_logging import COMPONENT_PREFIXES
from hermes_agent.logging import COMPONENT_PREFIXES
component_lower = component.lower()
if component_lower not in COMPONENT_PREFIXES:
available = ", ".join(sorted(COMPONENT_PREFIXES))

File diff suppressed because it is too large Load diff

View file

@ -15,15 +15,15 @@ import re
import time
from typing import Any, Dict, List, Optional, Tuple
from hermes_cli.config import (
from hermes_agent.cli.config import (
load_config,
save_config,
get_env_value,
save_env_value,
get_hermes_home, # noqa: F401 — used by test mocks
)
from hermes_cli.colors import Colors, color
from hermes_constants import display_hermes_home
from hermes_agent.cli.ui.colors import Colors, color
from hermes_agent.constants import display_hermes_home
logger = logging.getLogger(__name__)
@ -61,7 +61,7 @@ def _confirm(question: str, default: bool = True) -> bool:
def _prompt(question: str, *, password: bool = False, default: str = "") -> str:
from hermes_cli.cli_output import prompt as _shared_prompt
from hermes_agent.cli.ui.output import prompt as _shared_prompt
return _shared_prompt(question, default=default, password=password)
@ -165,7 +165,7 @@ def _probe_single_server(
Returns list of ``(tool_name, description)`` tuples.
Raises on connection failure.
"""
from tools.mcp_tool import (
from hermes_agent.tools.mcp.tool import (
_ensure_mcp_loop,
_run_on_mcp_loop,
_connect_server,
@ -279,7 +279,7 @@ def cmd_mcp_add(args):
_info(f"Starting OAuth flow for '{name}'...")
oauth_ok = False
try:
from tools.mcp_oauth_manager import get_manager
from hermes_agent.tools.mcp.oauth_manager import get_manager
oauth_auth = get_manager().get_or_build_provider(name, url, None)
if oauth_auth:
server_config["auth"] = "oauth"
@ -372,7 +372,7 @@ def cmd_mcp_add(args):
if choice in ("s", "select"):
# Interactive tool selection
from hermes_cli.curses_ui import curses_checklist
from hermes_agent.cli.ui.curses import curses_checklist
labels = [f"{t[0]}{t[1]}" for t in tools]
pre_selected = set(range(len(tools)))
@ -432,7 +432,7 @@ def cmd_mcp_remove(args):
# any provider instance cached in the current process (e.g. from an
# earlier `hermes mcp test` in the same session) is evicted too.
try:
from tools.mcp_oauth_manager import get_manager
from hermes_agent.tools.mcp.oauth_manager import get_manager
get_manager().remove(name)
_success("Cleaned up OAuth tokens")
except Exception:
@ -616,7 +616,7 @@ def cmd_mcp_login(args):
# Wipe both disk and in-memory cache so the next probe forces a fresh
# OAuth flow.
try:
from tools.mcp_oauth_manager import get_manager
from hermes_agent.tools.mcp.oauth_manager import get_manager
mgr = get_manager()
mgr.remove(name)
except Exception as exc:
@ -700,7 +700,7 @@ def cmd_mcp_configure(args):
print()
# Interactive checklist
from hermes_cli.curses_ui import curses_checklist
from hermes_agent.cli.ui.curses import curses_checklist
labels = [f"{t[0]}{t[1]}" for t in all_tools]
@ -742,7 +742,7 @@ def mcp_command(args):
action = getattr(args, "mcp_action", None)
if action == "serve":
from mcp_serve import run_mcp_server
from hermes_agent.tools.mcp.serve import run_mcp_server
run_mcp_server(verbose=getattr(args, "verbose", False))
return

View file

@ -12,7 +12,7 @@ import os
import sys
from pathlib import Path
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
# ---------------------------------------------------------------------------
@ -25,7 +25,7 @@ def _curses_select(title: str, items: list[tuple[str, str]], default: int = 0) -
items: list of (label, description) tuples.
Returns selected index, or default on escape/quit.
"""
from hermes_cli.curses_ui import curses_radiolist
from hermes_agent.cli.ui.curses import curses_radiolist
# Format (label, desc) tuples into display strings
display_items = [
f"{label} {desc}" if desc else label
@ -58,7 +58,7 @@ def _prompt(label: str, default: str | None = None, secret: bool = False) -> str
def _install_dependencies(provider_name: str) -> None:
"""Install pip dependencies declared in plugin.yaml."""
import subprocess
from plugins.memory import find_provider_dir
from hermes_agent.plugins.memory import find_provider_dir
plugin_dir = find_provider_dir(provider_name)
if not plugin_dir:
@ -148,7 +148,7 @@ def _get_available_providers() -> list:
Returns list of (name, description, provider_instance) tuples.
"""
try:
from plugins.memory import discover_memory_providers, load_memory_provider
from hermes_agent.plugins.memory import discover_memory_providers, load_memory_provider
raw = discover_memory_providers()
except Exception:
raw = []
@ -184,7 +184,7 @@ def _get_available_providers() -> list:
def cmd_setup_provider(provider_name: str) -> None:
"""Run memory setup for a specific provider, skipping the picker."""
from hermes_cli.config import load_config, save_config
from hermes_agent.cli.config import load_config, save_config
providers = _get_available_providers()
match = None
@ -220,7 +220,7 @@ def cmd_setup_provider(provider_name: str) -> None:
def cmd_setup(args) -> None:
"""Interactive memory provider setup wizard."""
from hermes_cli.config import load_config, save_config
from hermes_agent.cli.config import load_config, save_config
providers = _get_available_providers()
@ -386,7 +386,7 @@ def _write_env_vars(env_path: Path, env_writes: dict) -> None:
def cmd_status(args) -> None:
"""Show current memory provider config."""
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
config = load_config()
mem_config = config.get("memory", {})

View file

@ -16,7 +16,7 @@ from difflib import get_close_matches
from pathlib import Path
from typing import Any, NamedTuple, Optional
from hermes_cli import __version__ as _HERMES_VERSION
from hermes_agent.cli import __version__ as _HERMES_VERSION
# Identify ourselves so endpoints fronted by Cloudflare's Browser Integrity
# Check (error 1010) don't reject the default ``Python-urllib/*`` signature.
@ -101,7 +101,7 @@ def _codex_curated_models() -> list[str]:
This keeps the gateway /model picker in sync with the CLI `hermes model`
flow without maintaining a separate static list.
"""
from hermes_cli.codex_models import DEFAULT_CODEX_MODELS, _add_forward_compat_models
from hermes_agent.cli.models.codex import DEFAULT_CODEX_MODELS, _add_forward_compat_models
return _add_forward_compat_models(list(DEFAULT_CODEX_MODELS))
@ -488,7 +488,7 @@ def check_nous_free_tier() -> bool:
return cached_result
try:
from hermes_cli.auth import get_provider_auth_state, resolve_nous_runtime_credentials
from hermes_agent.cli.auth.auth import get_provider_auth_state, resolve_nous_runtime_credentials
# Ensure we have a fresh token (triggers refresh if needed)
resolve_nous_runtime_credentials(min_key_ttl_seconds=60)
@ -583,7 +583,7 @@ def fetch_nous_recommended_models(
def _resolve_nous_portal_url() -> str:
"""Best-effort lookup of the Portal base URL the user is authed against."""
try:
from hermes_cli.auth import (
from hermes_agent.cli.auth.auth import (
DEFAULT_NOUS_PORTAL_URL,
get_provider_auth_state,
)
@ -912,7 +912,7 @@ def fetch_ai_gateway_models(
if _ai_gateway_catalog_cache is not None and not force_refresh:
return list(_ai_gateway_catalog_cache)
from hermes_constants import AI_GATEWAY_BASE_URL
from hermes_agent.constants import AI_GATEWAY_BASE_URL
fallback = list(VERCEL_AI_GATEWAY_MODELS)
preferred_ids = [mid for mid, _ in fallback]
@ -1133,7 +1133,7 @@ def fetch_ai_gateway_pricing(
``prompt`` / ``completion``. This translates. Cache read/write field names
already match.
"""
from hermes_constants import AI_GATEWAY_BASE_URL
from hermes_agent.constants import AI_GATEWAY_BASE_URL
cache_key = AI_GATEWAY_BASE_URL.rstrip("/")
if not force_refresh and cache_key in _pricing_cache:
@ -1180,7 +1180,7 @@ def _resolve_openrouter_api_key() -> str:
def _resolve_nous_pricing_credentials() -> tuple[str, str]:
"""Return ``(api_key, base_url)`` for Nous Portal pricing, or empty strings."""
try:
from hermes_cli.auth import resolve_nous_runtime_credentials
from hermes_agent.cli.auth.auth import resolve_nous_runtime_credentials
creds = resolve_nous_runtime_credentials()
if creds:
return (creds.get("api_key", ""), creds.get("base_url", ""))
@ -1248,7 +1248,7 @@ def list_available_providers() -> list[dict[str, str]]:
# Check if this provider has credentials available
has_creds = False
try:
from hermes_cli.auth import get_auth_status, has_usable_secret
from hermes_agent.cli.auth.auth import get_auth_status, has_usable_secret
if pid == "custom":
custom_base_url = _get_custom_base_url() or ""
has_creds = bool(custom_base_url.strip())
@ -1307,7 +1307,7 @@ def parse_model_input(raw: str, current_provider: str) -> tuple[str, str]:
def _get_custom_base_url() -> str:
"""Get the custom endpoint base_url from config.yaml."""
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
config = load_config()
model_cfg = config.get("model", {})
if isinstance(model_cfg, dict):
@ -1401,7 +1401,7 @@ def detect_provider_for_model(
# credential pool, or auth store entries.
has_creds = False
try:
from hermes_cli.auth import PROVIDER_REGISTRY
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
pconfig = PROVIDER_REGISTRY.get(direct_match)
if pconfig:
for env_var in pconfig.api_key_env_vars:
@ -1414,7 +1414,7 @@ def detect_provider_for_model(
# Claude Code tokens, and other non-env-var credentials (#10300).
if not has_creds:
try:
from agent.credential_pool import load_pool
from hermes_agent.providers.credential_pool import load_pool
pool = load_pool(direct_match)
if pool.has_credentials():
has_creds = True
@ -1422,7 +1422,7 @@ def detect_provider_for_model(
pass
if not has_creds:
try:
from hermes_cli.auth import _load_auth_store
from hermes_agent.cli.auth.auth import _load_auth_store
store = _load_auth_store()
if direct_match in store.get("providers", {}) or direct_match in store.get("credential_pool", {}):
has_creds = True
@ -1572,7 +1572,7 @@ def resolve_fast_mode_overrides(model_id: Optional[str]) -> dict[str, Any] | Non
def _resolve_copilot_catalog_api_key() -> str:
"""Best-effort GitHub token for fetching the Copilot model catalog."""
try:
from hermes_cli.auth import resolve_api_key_provider_credentials
from hermes_agent.cli.auth.auth import resolve_api_key_provider_credentials
creds = resolve_api_key_provider_credentials("copilot")
return str(creds.get("api_key") or "").strip()
@ -1590,7 +1590,7 @@ def provider_model_ids(provider: Optional[str], *, force_refresh: bool = False)
if normalized == "openrouter":
return model_ids(force_refresh=force_refresh)
if normalized == "openai-codex":
from hermes_cli.codex_models import get_codex_model_ids
from hermes_agent.cli.models.codex import get_codex_model_ids
return get_codex_model_ids()
if normalized in {"copilot", "copilot-acp"}:
@ -1605,7 +1605,7 @@ def provider_model_ids(provider: Optional[str], *, force_refresh: bool = False)
if normalized == "nous":
# Try live Nous Portal /models endpoint
try:
from hermes_cli.auth import fetch_nous_models, resolve_nous_runtime_credentials
from hermes_agent.cli.auth.auth import fetch_nous_models, resolve_nous_runtime_credentials
creds = resolve_nous_runtime_credentials()
if creds:
live = fetch_nous_models(api_key=creds.get("api_key", ""), inference_base_url=creds.get("base_url", ""))
@ -1647,7 +1647,7 @@ def _fetch_anthropic_models(timeout: float = 5.0) -> Optional[list[str]]:
Claude Code auto-discovery). Returns sorted model IDs or None.
"""
try:
from agent.anthropic_adapter import resolve_anthropic_token, _is_oauth_token
from hermes_agent.providers.anthropic_adapter import resolve_anthropic_token, _is_oauth_token
except ImportError:
return None
@ -1658,7 +1658,7 @@ def _fetch_anthropic_models(timeout: float = 5.0) -> Optional[list[str]]:
headers: dict[str, str] = {"anthropic-version": "2023-06-01"}
if _is_oauth_token(token):
headers["Authorization"] = f"Bearer {token}"
from agent.anthropic_adapter import _COMMON_BETAS, _OAUTH_ONLY_BETAS
from hermes_agent.providers.anthropic_adapter import _COMMON_BETAS, _OAUTH_ONLY_BETAS
headers["anthropic-beta"] = ",".join(_COMMON_BETAS + _OAUTH_ONLY_BETAS)
else:
headers["x-api-key"] = token
@ -1701,7 +1701,7 @@ def copilot_default_headers() -> dict[str, str]:
Copilot CLI send on every request.
"""
try:
from hermes_cli.copilot_auth import copilot_request_headers
from hermes_agent.cli.auth.copilot import copilot_request_headers
return copilot_request_headers(is_agent_turn=True)
except ImportError:
return {
@ -2117,7 +2117,7 @@ def _fetch_ai_gateway_models(timeout: float = 5.0) -> Optional[list[str]]:
return None
base_url = os.getenv("AI_GATEWAY_BASE_URL", "").strip()
if not base_url:
from hermes_constants import AI_GATEWAY_BASE_URL
from hermes_agent.constants import AI_GATEWAY_BASE_URL
base_url = AI_GATEWAY_BASE_URL
url = base_url.rstrip("/") + "/models"
@ -2161,7 +2161,7 @@ _OLLAMA_CLOUD_CACHE_TTL = 3600 # 1 hour
def _ollama_cloud_cache_path() -> Path:
"""Return the path for the Ollama Cloud model cache."""
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
return get_hermes_home() / "ollama_cloud_models_cache.json"
@ -2195,7 +2195,7 @@ def _load_ollama_cloud_cache(*, ignore_ttl: bool = False) -> Optional[dict]:
def _save_ollama_cloud_cache(models: list[str]) -> None:
"""Persist the merged Ollama Cloud model list to disk."""
try:
from utils import atomic_json_write
from hermes_agent.utils import atomic_json_write
cache_path = _ollama_cloud_cache_path()
cache_path.parent.mkdir(parents=True, exist_ok=True)
atomic_json_write(cache_path, {"models": models, "cached_at": time.time()}, indent=None)
@ -2240,7 +2240,7 @@ def fetch_ollama_cloud_models(
# 3. models.dev registry
mdev_models: list[str] = []
try:
from agent.models_dev import list_agentic_models
from hermes_agent.providers.metadata_dev import list_agentic_models
mdev_models = list_agentic_models("ollama-cloud")
except Exception:
pass
@ -2510,7 +2510,7 @@ def validate_requested_model(
# AWS SDK control plane (ListFoundationModels + ListInferenceProfiles).
if normalized == "bedrock":
try:
from agent.bedrock_adapter import discover_bedrock_models, resolve_bedrock_region
from hermes_agent.providers.bedrock_adapter import discover_bedrock_models, resolve_bedrock_region
region = resolve_bedrock_region()
discovered = discover_bedrock_models(region)
discovered_ids = {m["id"] for m in discovered}

View file

@ -184,7 +184,7 @@ def _normalize_provider_alias(provider_name: str) -> str:
if not raw:
return raw
try:
from hermes_cli.models import normalize_provider
from hermes_agent.cli.models.models import normalize_provider
return normalize_provider(raw)
except Exception:
@ -382,7 +382,7 @@ def normalize_model_for_provider(model_input: str, target_provider: str) -> str:
# HTTP 400 "model_not_supported". See issue #6879.
if provider in {"copilot", "copilot-acp"}:
try:
from hermes_cli.models import normalize_copilot_model_id
from hermes_agent.cli.models.models import normalize_copilot_model_id
normalized = normalize_copilot_model_id(name)
if normalized:

View file

@ -25,17 +25,17 @@ import re
from dataclasses import dataclass
from typing import List, NamedTuple, Optional
from hermes_cli.providers import (
from hermes_agent.cli.providers import (
custom_provider_slug,
determine_api_mode,
get_label,
is_aggregator,
resolve_provider_full,
)
from hermes_cli.model_normalize import (
from hermes_agent.cli.models.normalize import (
normalize_model_for_provider,
)
from agent.models_dev import (
from hermes_agent.providers.metadata_dev import (
ModelCapabilities,
ModelInfo,
get_model_capabilities,
@ -193,7 +193,7 @@ def _load_direct_aliases() -> dict[str, DirectAlias]:
"""
merged = dict(_BUILTIN_DIRECT_ALIASES)
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
cfg = load_config()
user_aliases = cfg.get("model_aliases")
if isinstance(user_aliases, dict):
@ -456,13 +456,13 @@ def switch_model(
Returns:
ModelSwitchResult with all information the caller needs.
"""
from hermes_cli.models import (
from hermes_agent.cli.models.models import (
copilot_model_api_mode,
detect_provider_for_model,
validate_requested_model,
opencode_model_api_mode,
)
from hermes_cli.runtime_provider import resolve_runtime_provider
from hermes_agent.cli.runtime_provider import resolve_runtime_provider
resolved_alias = ""
new_model = raw_input.strip()
@ -486,7 +486,7 @@ def switch_model(
)
# Check for common config issues that cause provider resolution failures
try:
from hermes_cli.config import validate_config_structure
from hermes_agent.cli.config import validate_config_structure
_cfg_issues = validate_config_structure()
if _cfg_issues:
_switch_err += "\n\nRun 'hermes doctor' — config issues detected:"
@ -505,7 +505,7 @@ def switch_model(
# If no model specified, try auto-detect from endpoint
if not new_model:
if pdef.base_url:
from hermes_cli.runtime_provider import _auto_detect_local_model
from hermes_agent.cli.runtime_provider import _auto_detect_local_model
detected = _auto_detect_local_model(pdef.base_url)
if detected:
new_model = detected
@ -804,13 +804,13 @@ def list_authenticated_providers(
Only includes providers that have API keys set or are user-defined endpoints.
"""
import os
from agent.models_dev import (
from hermes_agent.providers.metadata_dev import (
PROVIDER_TO_MODELS_DEV,
fetch_models_dev,
get_provider_info as _mdev_pinfo,
)
from hermes_cli.auth import PROVIDER_REGISTRY
from hermes_cli.models import OPENROUTER_MODELS, _PROVIDER_MODELS
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
from hermes_agent.cli.models.models import OPENROUTER_MODELS, _PROVIDER_MODELS
results: List[dict] = []
seen_slugs: set = set() # lowercase-normalized to catch case variants (#9545)
@ -826,7 +826,7 @@ def list_authenticated_providers(
curated["nous"] = curated["openrouter"]
# Ollama Cloud uses dynamic discovery (no static curated list)
if "ollama-cloud" not in curated:
from hermes_cli.models import fetch_ollama_cloud_models
from hermes_agent.cli.models.models import fetch_ollama_cloud_models
curated["ollama-cloud"] = fetch_ollama_cloud_models()
# --- 1. Check Hermes-mapped providers ---
@ -878,8 +878,8 @@ def list_authenticated_providers(
seen_mdev_ids.add(mdev_id)
# --- 2. Check Hermes-only providers (nous, openai-codex, copilot, opencode-go) ---
from hermes_cli.providers import HERMES_OVERLAYS
from hermes_cli.auth import PROVIDER_REGISTRY as _auth_registry
from hermes_agent.cli.providers import HERMES_OVERLAYS
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY as _auth_registry
# Build reverse mapping: models.dev ID → Hermes provider ID.
# HERMES_OVERLAYS keys may be models.dev IDs (e.g. "github-copilot")
@ -913,7 +913,7 @@ def list_authenticated_providers(
# OAuth via external credential files).
if not has_creds:
try:
from hermes_cli.auth import _load_auth_store
from hermes_agent.cli.auth.auth import _load_auth_store
store = _load_auth_store()
providers_store = store.get("providers", {})
pool_store = store.get("credential_pool", {})
@ -930,7 +930,7 @@ def list_authenticated_providers(
# imports on demand but aren't in the raw auth.json yet.
if not has_creds:
try:
from agent.credential_pool import load_pool
from hermes_agent.providers.credential_pool import load_pool
pool = load_pool(hermes_slug)
if pool.has_credentials():
has_creds = True
@ -945,7 +945,7 @@ def list_authenticated_providers(
# configured.
if not has_creds and hermes_slug == "anthropic":
try:
from agent.anthropic_adapter import (
from hermes_agent.providers.anthropic_adapter import (
read_claude_code_credentials,
read_hermes_oauth_credentials,
)
@ -981,7 +981,7 @@ def list_authenticated_providers(
# in PROVIDER_TO_MODELS_DEV or HERMES_OVERLAYS (keeps /model in sync
# with `hermes model`).
try:
from hermes_cli.models import CANONICAL_PROVIDERS as _canon_provs
from hermes_agent.cli.models.models import CANONICAL_PROVIDERS as _canon_provs
except ImportError:
_canon_provs = []
@ -997,7 +997,7 @@ def list_authenticated_providers(
# Also check auth store and credential pool
if not _cp_has_creds:
try:
from hermes_cli.auth import _load_auth_store
from hermes_agent.cli.auth.auth import _load_auth_store
_cp_store = _load_auth_store()
_cp_providers_store = _cp_store.get("providers", {})
_cp_pool_store = _cp_store.get("credential_pool", {})
@ -1010,7 +1010,7 @@ def list_authenticated_providers(
pass
if not _cp_has_creds:
try:
from agent.credential_pool import load_pool
from hermes_agent.providers.credential_pool import load_pool
_cp_pool = load_pool(_cp.slug)
if _cp_pool.has_credentials():
_cp_has_creds = True

View file

@ -6,10 +6,10 @@ from dataclasses import dataclass
from pathlib import Path
from typing import Dict, Iterable, Optional, Set
from hermes_cli.auth import get_nous_auth_status
from hermes_cli.config import get_env_value, load_config
from tools.managed_tool_gateway import is_managed_tool_gateway_ready
from tools.tool_backend_helpers import (
from hermes_agent.cli.auth.auth import get_nous_auth_status
from hermes_agent.cli.config import get_env_value, load_config
from hermes_agent.tools.managed_gateway import is_managed_tool_gateway_ready
from hermes_agent.tools.backend_helpers import (
fal_key_is_configured,
has_direct_modal_credentials,
managed_nous_tools_enabled,
@ -82,7 +82,7 @@ def _model_config_dict(config: Dict[str, object]) -> Dict[str, object]:
def _toolset_enabled(config: Dict[str, object], toolset_key: str) -> bool:
from toolsets import resolve_toolset
from hermes_agent.tools.toolsets import resolve_toolset
platform_toolsets = config.get("platform_toolsets")
if not isinstance(platform_toolsets, dict) or not platform_toolsets:
@ -688,7 +688,7 @@ def prompt_enable_tool_gateway(config: Dict[str, object]) -> set[str]:
return set()
try:
from hermes_cli.setup import prompt_choice
from hermes_agent.cli.setup_wizard import prompt_choice
except Exception:
return set()
@ -766,7 +766,7 @@ def prompt_enable_tool_gateway(config: Dict[str, object]) -> set[str]:
changed = apply_gateway_defaults(config, to_apply)
if changed:
from hermes_cli.config import save_config
from hermes_agent.cli.config import save_config
save_config(config)
# Only report the tools that actually switched (not already-managed ones)
newly_switched = changed - set(already_managed)

View file

@ -10,7 +10,7 @@ Usage:
def pairing_command(args):
"""Handle hermes pairing subcommands."""
from gateway.pairing import PairingStore
from hermes_agent.gateway.pairing import PairingStore
store = PairingStore()
action = getattr(args, "pairing_action", None)

View file

@ -43,8 +43,8 @@ from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Set, Union
from hermes_constants import get_hermes_home
from utils import env_var_enabled
from hermes_agent.constants import get_hermes_home
from hermes_agent.utils import env_var_enabled
try:
import yaml
@ -73,7 +73,7 @@ VALID_HOOKS: Set[str] = {
"subagent_stop",
}
ENTRY_POINTS_GROUP = "hermes_agent.plugins"
ENTRY_POINTS_GROUP = "plugins"
_NS_PARENT = "hermes_plugins"
@ -91,7 +91,7 @@ def _get_disabled_plugins() -> set:
``plugins.enabled``.
"""
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
config = load_config()
disabled = config.get("plugins", {}).get("disabled", [])
return set(disabled) if isinstance(disabled, list) else set()
@ -114,7 +114,7 @@ def _get_enabled_plugins() -> Optional[set]:
* ``set(...)`` the concrete allow-list.
"""
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
config = load_config()
plugins_cfg = config.get("plugins")
if not isinstance(plugins_cfg, dict):
@ -207,7 +207,7 @@ class PluginContext:
emoji: str = "",
) -> None:
"""Register a tool in the global registry **and** track it as plugin-provided."""
from tools.registry import registry
from hermes_agent.tools.registry import registry
registry.register(
name=name,
@ -305,7 +305,7 @@ class PluginContext:
# Reject if it conflicts with a built-in command
try:
from hermes_cli.commands import resolve_command
from hermes_agent.cli.commands import resolve_command
if resolve_command(clean) is not None:
logger.warning(
"Plugin '%s' tried to register command '/%s' which conflicts "
@ -341,7 +341,7 @@ class PluginContext:
Returns:
JSON string from the tool handler (same format as model tool calls).
"""
from tools.registry import registry
from hermes_agent.tools.registry import registry
# Wire up parent agent context when available (CLI mode).
# In gateway mode _cli_ref is None — tools degrade gracefully
@ -372,7 +372,7 @@ class PluginContext:
)
return
# Defer the import to avoid circular deps at module level
from agent.context_engine import ContextEngine
from hermes_agent.agent.context.engine import ContextEngine
if not isinstance(engine, ContextEngine):
logger.warning(
"Plugin '%s' tried to register a context engine that does not "
@ -397,8 +397,8 @@ class PluginContext:
``config.yaml`` matches against when routing ``image_generate``
tool calls.
"""
from agent.image_gen_provider import ImageGenProvider
from agent.image_gen_registry import register_provider
from hermes_agent.agent.image_gen.provider import ImageGenProvider
from hermes_agent.agent.image_gen.registry import register_provider
if not isinstance(provider, ImageGenProvider):
logger.warning(
@ -452,7 +452,7 @@ class PluginContext:
ValueError: if *name* contains ``':'`` or invalid characters.
FileNotFoundError: if *path* does not exist.
"""
from agent.skill_utils import _NAMESPACE_RE
from hermes_agent.agent.skill_utils import _NAMESPACE_RE
if ":" in name:
raise ValueError(
@ -1087,7 +1087,7 @@ def get_plugin_toolsets() -> List[tuple]:
return []
try:
from tools.registry import registry
from hermes_agent.tools.registry import registry
except Exception:
return []

View file

@ -17,7 +17,7 @@ import sys
from pathlib import Path
from typing import Optional
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
logger = logging.getLogger(__name__)
@ -173,8 +173,8 @@ def _prompt_plugin_env_vars(manifest: dict, console) -> None:
if not requires_env:
return
from hermes_cli.config import get_env_value, save_env_value # noqa: F811
from hermes_constants import display_hermes_home
from hermes_agent.cli.config import get_env_value, save_env_value # noqa: F811
from hermes_agent.constants import display_hermes_home
# Normalise to list-of-dicts
env_specs: list[dict] = []
@ -360,7 +360,7 @@ def cmd_install(
)
sys.exit(1)
if mv_int > _SUPPORTED_MANIFEST_VERSION:
from hermes_cli.config import recommended_update_command
from hermes_agent.cli.config import recommended_update_command
console.print(
f"[red]Error:[/red] Plugin '{plugin_name}' requires manifest_version "
f"{mv}, but this installer only supports up to {_SUPPORTED_MANIFEST_VERSION}.\n"
@ -517,7 +517,7 @@ def _get_disabled_set() -> set:
listed in ``plugins.enabled``.
"""
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
config = load_config()
disabled = config.get("plugins", {}).get("disabled", [])
return set(disabled) if isinstance(disabled, list) else set()
@ -527,7 +527,7 @@ def _get_disabled_set() -> set:
def _save_disabled_set(disabled: set) -> None:
"""Write the disabled plugins list to config.yaml."""
from hermes_cli.config import load_config, save_config
from hermes_agent.cli.config import load_config, save_config
config = load_config()
if "plugins" not in config:
config["plugins"] = {}
@ -542,7 +542,7 @@ def _get_enabled_set() -> set:
the key is missing (same behaviour as "nothing enabled yet").
"""
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
config = load_config()
plugins_cfg = config.get("plugins", {})
if not isinstance(plugins_cfg, dict):
@ -555,7 +555,7 @@ def _get_enabled_set() -> set:
def _save_enabled_set(enabled: set) -> None:
"""Write the enabled plugins list to config.yaml."""
from hermes_cli.config import load_config, save_config
from hermes_agent.cli.config import load_config, save_config
config = load_config()
if "plugins" not in config:
config["plugins"] = {}
@ -631,7 +631,7 @@ def _plugin_exists(name: str) -> bool:
return True
# Bundled: <repo>/plugins/<name>/
from pathlib import Path as _P
import hermes_cli
import hermes_agent.cli
repo_plugins = _P(hermes_cli.__file__).resolve().parent.parent / "plugins"
if repo_plugins.is_dir():
candidate = repo_plugins / name
@ -659,7 +659,7 @@ def _discover_all_plugins() -> list:
seen: dict = {} # name -> (name, version, description, source, path)
# Bundled (<repo>/plugins/<name>/), excluding memory/ and context_engine/
import hermes_cli
import hermes_agent.cli
repo_plugins = Path(hermes_cli.__file__).resolve().parent.parent / "plugins"
for base, source in ((repo_plugins, "bundled"), (_plugins_dir(), "user")):
if not base.is_dir():
@ -743,7 +743,7 @@ def cmd_list() -> None:
def _discover_memory_providers() -> list[tuple[str, str]]:
"""Return [(name, description), ...] for available memory providers."""
try:
from plugins.memory import discover_memory_providers
from hermes_agent.plugins.memory import discover_memory_providers
return [(name, desc) for name, desc, _avail in discover_memory_providers()]
except Exception:
return []
@ -752,7 +752,7 @@ def _discover_memory_providers() -> list[tuple[str, str]]:
def _discover_context_engines() -> list[tuple[str, str]]:
"""Return [(name, description), ...] for available context engines."""
try:
from plugins.context_engine import discover_context_engines
from hermes_agent.plugins.context_engine import discover_context_engines
return [(name, desc) for name, desc, _avail in discover_context_engines()]
except Exception:
return []
@ -761,7 +761,7 @@ def _discover_context_engines() -> list[tuple[str, str]]:
def _get_current_memory_provider() -> str:
"""Return the current memory.provider from config (empty = built-in)."""
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
config = load_config()
return config.get("memory", {}).get("provider", "") or ""
except Exception:
@ -771,7 +771,7 @@ def _get_current_memory_provider() -> str:
def _get_current_context_engine() -> str:
"""Return the current context.engine from config."""
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
config = load_config()
return config.get("context", {}).get("engine", "compressor") or "compressor"
except Exception:
@ -780,7 +780,7 @@ def _get_current_context_engine() -> str:
def _save_memory_provider(name: str) -> None:
"""Persist memory.provider to config.yaml."""
from hermes_cli.config import load_config, save_config
from hermes_agent.cli.config import load_config, save_config
config = load_config()
if "memory" not in config:
config["memory"] = {}
@ -790,7 +790,7 @@ def _save_memory_provider(name: str) -> None:
def _save_context_engine(name: str) -> None:
"""Persist context.engine to config.yaml."""
from hermes_cli.config import load_config, save_config
from hermes_agent.cli.config import load_config, save_config
config = load_config()
if "context" not in config:
config["context"] = {}
@ -800,7 +800,7 @@ def _save_context_engine(name: str) -> None:
def _configure_memory_provider() -> bool:
"""Launch a radio picker for memory providers. Returns True if changed."""
from hermes_cli.curses_ui import curses_radiolist
from hermes_agent.cli.ui.curses import curses_radiolist
current = _get_current_memory_provider()
providers = _discover_memory_providers()
@ -838,7 +838,7 @@ def _configure_memory_provider() -> bool:
def _configure_context_engine() -> bool:
"""Launch a radio picker for context engines. Returns True if changed."""
from hermes_cli.curses_ui import curses_radiolist
from hermes_agent.cli.ui.curses import curses_radiolist
current = _get_current_context_engine()
engines = _discover_context_engines()
@ -938,7 +938,7 @@ def cmd_toggle() -> None:
def _run_composite_ui(curses, plugin_names, plugin_labels, plugin_selected,
disabled, categories, console):
"""Custom curses screen with checkboxes + category action rows."""
from hermes_cli.curses_ui import flush_stdin
from hermes_agent.cli.ui.curses import flush_stdin
chosen = set(plugin_selected)
n_plugins = len(plugin_names)
@ -1188,7 +1188,7 @@ def _run_composite_ui(curses, plugin_names, plugin_labels, plugin_selected,
def _run_composite_fallback(plugin_names, plugin_labels, plugin_selected,
disabled, categories, console):
"""Text-based fallback for the composite plugins UI."""
from hermes_cli.colors import Colors, color
from hermes_agent.cli.ui.colors import Colors, color
print(color("\n Plugins", Colors.YELLOW))

View file

@ -84,7 +84,7 @@ _DEFAULT_EXPORT_EXCLUDE_ROOT = frozenset({
"node_modules", # npm packages
# Databases & runtime state
"state.db", "state.db-shm", "state.db-wal",
"hermes_state.db",
"state.db",
"response_store.db", "response_store.db-shm", "response_store.db-wal",
"gateway.pid", "gateway_state.json", "processes.json",
"auth.json", # API keys, OAuth tokens, credential pools
@ -138,7 +138,7 @@ def _get_default_hermes_home() -> Path:
In Docker/custom deployments where HERMES_HOME is outside ``~/.hermes``
(e.g. ``/opt/data``), returns HERMES_HOME directly.
"""
from hermes_constants import get_default_hermes_root
from hermes_agent.constants import get_default_hermes_root
return get_default_hermes_root()
@ -301,7 +301,7 @@ def _read_config_model(profile_dir: Path) -> tuple:
def _check_gateway_running(profile_dir: Path) -> bool:
"""Check if a gateway is running for a given profile directory."""
try:
from gateway.status import get_running_pid
from hermes_agent.gateway.status import get_running_pid
return get_running_pid(profile_dir / "gateway.pid", cleanup_stale=False) is not None
except Exception:
return False
@ -413,7 +413,7 @@ def create_profile(
if clone_from is not None or clone_all or clone_config:
if clone_from is None:
# Default: clone from active profile
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
source_dir = get_hermes_home()
else:
validate_profile_name(clone_from)
@ -455,7 +455,7 @@ def create_profile(
soul_path = profile_dir / "SOUL.md"
if not soul_path.exists():
try:
from hermes_cli.default_soul import DEFAULT_SOUL_MD
from hermes_agent.cli.default_soul import DEFAULT_SOUL_MD
soul_path.write_text(DEFAULT_SOUL_MD, encoding="utf-8")
except Exception:
pass # best-effort — don't fail profile creation over this
@ -597,7 +597,7 @@ def _cleanup_gateway_service(name: str, profile_dir: Path) -> None:
old_home = os.environ.get("HERMES_HOME")
try:
os.environ["HERMES_HOME"] = str(profile_dir)
from hermes_cli.gateway import get_service_name, get_launchd_plist_path
from hermes_agent.cli.gateway import get_service_name, get_launchd_plist_path
if _platform.system() == "Linux":
svc_name = get_service_name()
@ -720,7 +720,7 @@ def get_active_profile_name() -> str:
Returns the profile name if HERMES_HOME points into ``~/.hermes/profiles/<name>``.
Returns ``"custom"`` if HERMES_HOME is set to an unrecognized path.
"""
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
hermes_home = get_hermes_home()
resolved = hermes_home.resolve()

View file

@ -23,7 +23,7 @@ import logging
from dataclasses import dataclass
from typing import Any, Dict, List, Optional, Tuple
from utils import base_url_host_matches, base_url_hostname
from hermes_agent.utils import base_url_host_matches, base_url_hostname
logger = logging.getLogger(__name__)
@ -341,7 +341,7 @@ def get_provider(name: str) -> Optional[ProviderDef]:
# Try to get models.dev data
try:
from agent.models_dev import get_provider_info as _mdev_provider
from hermes_agent.providers.metadata_dev import get_provider_info as _mdev_provider
mdev_info = _mdev_provider(canonical)
except Exception:
mdev_info = None
@ -596,7 +596,7 @@ def resolve_provider_full(
# 3. Try models.dev directly (for providers not in our ALIASES)
try:
from agent.models_dev import get_provider_info as _mdev_provider
from hermes_agent.providers.metadata_dev import get_provider_info as _mdev_provider
mdev_info = _mdev_provider(canonical)
if mdev_info is not None:
return ProviderDef(

File diff suppressed because it is too large Load diff

View file

@ -9,9 +9,9 @@ from typing import Any, Dict, Optional
logger = logging.getLogger(__name__)
from hermes_cli import auth as auth_mod
from agent.credential_pool import CredentialPool, PooledCredential, get_custom_provider_pool_key, load_pool
from hermes_cli.auth import (
from hermes_agent.cli import auth as auth_mod
from hermes_agent.providers.credential_pool import CredentialPool, PooledCredential, get_custom_provider_pool_key, load_pool
from hermes_agent.cli.auth.auth import (
AuthError,
DEFAULT_CODEX_BASE_URL,
DEFAULT_QWEN_BASE_URL,
@ -27,9 +27,9 @@ from hermes_cli.auth import (
resolve_external_process_provider_credentials,
has_usable_secret,
)
from hermes_cli.config import get_compatible_custom_providers, load_config
from hermes_constants import OPENROUTER_BASE_URL
from utils import base_url_host_matches, base_url_hostname
from hermes_agent.cli.config import get_compatible_custom_providers, load_config
from hermes_agent.constants import OPENROUTER_BASE_URL
from hermes_agent.utils import base_url_host_matches, base_url_hostname
def _normalize_custom_provider_name(value: str) -> str:
@ -134,7 +134,7 @@ def _copilot_runtime_api_mode(model_cfg: Dict[str, Any], api_key: str) -> str:
return "chat_completions"
try:
from hermes_cli.models import copilot_model_api_mode
from hermes_agent.cli.models.models import copilot_model_api_mode
return copilot_model_api_mode(model_name, api_key=api_key)
except Exception:
@ -206,7 +206,7 @@ def _resolve_runtime_from_pool_entry(
if configured_mode and _provider_supports_explicit_api_mode(provider, configured_provider):
api_mode = configured_mode
elif provider in ("opencode-zen", "opencode-go"):
from hermes_cli.models import opencode_model_api_mode
from hermes_agent.cli.models.models import opencode_model_api_mode
api_mode = opencode_model_api_mode(provider, model_cfg.get("default", ""))
else:
# Auto-detect Anthropic-compatible endpoints (/anthropic suffix,
@ -567,7 +567,7 @@ def _resolve_explicit_runtime(
base_url = explicit_base_url or cfg_base_url or "https://api.anthropic.com"
api_key = explicit_api_key
if not api_key:
from agent.anthropic_adapter import resolve_anthropic_token
from hermes_agent.providers.anthropic_adapter import resolve_anthropic_token
api_key = resolve_anthropic_token()
if not api_key:
@ -870,7 +870,7 @@ def resolve_runtime_provider(
# Anthropic (native Messages API)
if provider == "anthropic":
from agent.anthropic_adapter import resolve_anthropic_token
from hermes_agent.providers.anthropic_adapter import resolve_anthropic_token
token = resolve_anthropic_token()
if not token:
raise AuthError(
@ -896,7 +896,7 @@ def resolve_runtime_provider(
# AWS Bedrock (native Converse API via boto3)
if provider == "bedrock":
from agent.bedrock_adapter import (
from hermes_agent.providers.bedrock_adapter import (
has_aws_credentials,
resolve_aws_auth_env_var,
resolve_bedrock_region,
@ -989,7 +989,7 @@ def resolve_runtime_provider(
if configured_mode and _provider_supports_explicit_api_mode(provider, configured_provider):
api_mode = configured_mode
elif provider in ("opencode-zen", "opencode-go"):
from hermes_cli.models import opencode_model_api_mode
from hermes_agent.cli.models.models import opencode_model_api_mode
api_mode = opencode_model_api_mode(provider, model_cfg.get("default", ""))
else:
# Auto-detect Anthropic-compatible endpoints by URL convention

View file

@ -20,10 +20,10 @@ import copy
from pathlib import Path
from typing import Optional, Dict, Any
from hermes_cli.nous_subscription import get_nous_subscription_features
from tools.tool_backend_helpers import managed_nous_tools_enabled
from utils import base_url_hostname
from hermes_constants import get_optional_skills_dir
from hermes_agent.cli.nous_subscription import get_nous_subscription_features
from hermes_agent.tools.backend_helpers import managed_nous_tools_enabled
from hermes_agent.utils import base_url_hostname
from hermes_agent.constants import get_optional_skills_dir
logger = logging.getLogger(__name__)
@ -59,7 +59,7 @@ def _supports_same_provider_pool_setup(provider: str) -> bool:
return False
if provider == "openrouter":
return True
from hermes_cli.auth import PROVIDER_REGISTRY
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
pconfig = PROVIDER_REGISTRY.get(provider)
if not pconfig:
@ -129,7 +129,7 @@ def _set_reasoning_effort(config: Dict[str, Any], effort: str) -> None:
# Import config helpers
from hermes_cli.config import (
from hermes_agent.cli.config import (
DEFAULT_CONFIG,
get_hermes_home,
get_config_path,
@ -142,7 +142,7 @@ from hermes_cli.config import (
)
# display_hermes_home imported lazily at call sites (stale-module safety during hermes update)
from hermes_cli.colors import Colors, color
from hermes_agent.cli.ui.colors import Colors, color
def print_header(title: str):
@ -151,7 +151,7 @@ def print_header(title: str):
print(color(f"{title}", Colors.CYAN, Colors.BOLD))
from hermes_cli.cli_output import ( # noqa: E402
from hermes_agent.cli.ui.output import ( # noqa: E402
print_error,
print_info,
print_success,
@ -212,7 +212,7 @@ def prompt(question: str, default: str = None, password: bool = False) -> str:
def _curses_prompt_choice(question: str, choices: list, default: int = 0, description: str | None = None) -> int:
"""Single-select menu using curses. Delegates to curses_radiolist."""
from hermes_cli.curses_ui import curses_radiolist
from hermes_agent.cli.ui.curses import curses_radiolist
return curses_radiolist(question, choices, selected=default, cancel_returns=-1, description=description)
@ -302,7 +302,7 @@ def prompt_checklist(title: str, items: list, pre_selected: list = None) -> list
if pre_selected is None:
pre_selected = []
from hermes_cli.curses_ui import curses_checklist
from hermes_agent.cli.ui.curses import curses_checklist
chosen = curses_checklist(
title,
@ -352,7 +352,7 @@ def _print_setup_summary(config: dict, hermes_home):
# Vision — use the same runtime resolver as the actual vision tools
try:
from agent.auxiliary_client import get_available_vision_backends
from hermes_agent.providers.auxiliary import get_available_vision_backends
_vision_backends = get_available_vision_backends()
except Exception:
@ -419,8 +419,8 @@ def _print_setup_summary(config: dict, hermes_home):
# setups don't show as "missing FAL_KEY".
_img_backend = None
try:
from agent.image_gen_registry import list_providers
from hermes_cli.plugins import _ensure_plugins_discovered
from hermes_agent.agent.image_gen.registry import list_providers
from hermes_agent.cli.plugins import _ensure_plugins_discovered
_ensure_plugins_discovered()
for _p in list_providers():
@ -536,7 +536,7 @@ def _print_setup_summary(config: dict, hermes_home):
print_warning(
"Some tools are disabled. Run 'hermes setup tools' to configure them,"
)
from hermes_constants import display_hermes_home as _dhh
from hermes_agent.constants import display_hermes_home as _dhh
print_warning(f"or edit {_dhh()}/.env directly to add the missing API keys.")
print()
@ -560,7 +560,7 @@ def _print_setup_summary(config: dict, hermes_home):
print()
# Show file locations prominently
from hermes_constants import display_hermes_home as _dhh
from hermes_agent.constants import display_hermes_home as _dhh
print(color(f"📁 All your files are in {_dhh()}/:", Colors.CYAN, Colors.BOLD))
print()
print(f" {color('Settings:', Colors.YELLOW)} {get_config_path()}")
@ -665,7 +665,7 @@ def setup_model_provider(config: dict, *, quick: bool = False):
When *quick* is True, skips credential rotation, vision, and TTS
configuration used by the streamlined first-time quick setup.
"""
from hermes_cli.config import load_config, save_config
from hermes_agent.cli.config import load_config, save_config
print_header("Inference Provider")
print_info("Choose how to connect to your main chat model.")
@ -674,7 +674,7 @@ def setup_model_provider(config: dict, *, quick: bool = False):
# Delegate to the shared hermes model flow — handles provider picker,
# credential prompting, model selection, and config persistence.
from hermes_cli.main import select_provider_and_model
from hermes_agent.cli.main import select_provider_and_model
try:
select_provider_and_model()
except (SystemExit, KeyboardInterrupt):
@ -708,8 +708,8 @@ def setup_model_provider(config: dict, *, quick: bool = False):
if not quick and _supports_same_provider_pool_setup(selected_provider):
try:
from types import SimpleNamespace
from agent.credential_pool import load_pool
from hermes_cli.auth_commands import auth_add_command
from hermes_agent.providers.credential_pool import load_pool
from hermes_agent.cli.auth.commands import auth_add_command
pool = load_pool(selected_provider)
entries = pool.entries()
@ -786,7 +786,7 @@ def setup_model_provider(config: dict, *, quick: bool = False):
_vision_needs_setup = False
else:
try:
from agent.auxiliary_client import get_available_vision_backends
from hermes_agent.providers.auxiliary import get_available_vision_backends
_vision_backends = set(get_available_vision_backends())
except Exception:
_vision_backends = set()
@ -1075,7 +1075,7 @@ def _setup_tts_provider(config: dict):
save_env_value("XAI_API_KEY", api_key)
print_success("xAI TTS API key saved")
else:
from hermes_constants import display_hermes_home as _dhh
from hermes_agent.constants import display_hermes_home as _dhh
print_warning(
"No xAI API key provided for TTS. Configure XAI_API_KEY via "
f"hermes setup model or {_dhh()}/.env to use xAI TTS. "
@ -1284,8 +1284,8 @@ def setup_terminal_backend(config: dict):
elif selected_backend == "modal":
print_success("Terminal backend: Modal")
print_info("Serverless cloud sandboxes. Each session gets its own container.")
from tools.managed_tool_gateway import is_managed_tool_gateway_ready
from tools.tool_backend_helpers import normalize_modal_mode
from hermes_agent.tools.managed_gateway import is_managed_tool_gateway_ready
from hermes_agent.tools.backend_helpers import normalize_modal_mode
managed_modal_available = bool(
managed_nous_tools_enabled()
@ -2040,49 +2040,49 @@ def _setup_whatsapp():
def _setup_weixin():
"""Configure Weixin (personal WeChat) via iLink Bot API QR login."""
from hermes_cli.gateway import _setup_weixin as _gateway_setup_weixin
from hermes_agent.cli.gateway import _setup_weixin as _gateway_setup_weixin
_gateway_setup_weixin()
def _setup_signal():
"""Configure Signal via gateway setup."""
from hermes_cli.gateway import _setup_signal as _gateway_setup_signal
from hermes_agent.cli.gateway import _setup_signal as _gateway_setup_signal
_gateway_setup_signal()
def _setup_email():
"""Configure Email via gateway setup."""
from hermes_cli.gateway import _setup_email as _gateway_setup_email
from hermes_agent.cli.gateway import _setup_email as _gateway_setup_email
_gateway_setup_email()
def _setup_sms():
"""Configure SMS (Twilio) via gateway setup."""
from hermes_cli.gateway import _setup_sms as _gateway_setup_sms
from hermes_agent.cli.gateway import _setup_sms as _gateway_setup_sms
_gateway_setup_sms()
def _setup_dingtalk():
"""Configure DingTalk via gateway setup."""
from hermes_cli.gateway import _setup_dingtalk as _gateway_setup_dingtalk
from hermes_agent.cli.gateway import _setup_dingtalk as _gateway_setup_dingtalk
_gateway_setup_dingtalk()
def _setup_feishu():
"""Configure Feishu / Lark via gateway setup."""
from hermes_cli.gateway import _setup_feishu as _gateway_setup_feishu
from hermes_agent.cli.gateway import _setup_feishu as _gateway_setup_feishu
_gateway_setup_feishu()
def _setup_wecom():
"""Configure WeCom (Enterprise WeChat) via gateway setup."""
from hermes_cli.gateway import _setup_wecom as _gateway_setup_wecom
from hermes_agent.cli.gateway import _setup_wecom as _gateway_setup_wecom
_gateway_setup_wecom()
def _setup_wecom_callback():
"""Configure WeCom Callback (self-built app) via gateway setup."""
from hermes_cli.gateway import _setup_wecom_callback as _gw_setup
from hermes_agent.cli.gateway import _setup_wecom_callback as _gw_setup
_gw_setup()
@ -2155,7 +2155,7 @@ def _setup_bluebubbles():
def _setup_qqbot():
"""Configure QQ Bot (Official API v2) via gateway setup."""
from hermes_cli.gateway import _setup_qqbot as _gateway_setup_qqbot
from hermes_agent.cli.gateway import _setup_qqbot as _gateway_setup_qqbot
_gateway_setup_qqbot()
@ -2194,7 +2194,7 @@ def _setup_webhooks():
save_env_value("WEBHOOK_ENABLED", "true")
print()
print_success("Webhooks enabled! Next steps:")
from hermes_constants import display_hermes_home as _dhh
from hermes_agent.constants import display_hermes_home as _dhh
print_info(f" 1. Define webhook routes in {_dhh()}/config.yaml")
print_info(" 2. Point your service (GitHub, GitLab, etc.) at:")
print_info(" http://your-server:8644/webhooks/<route-name>")
@ -2318,7 +2318,7 @@ def setup_gateway(config: dict):
_is_linux = _platform.system() == "Linux"
_is_macos = _platform.system() == "Darwin"
from hermes_cli.gateway import (
from hermes_agent.cli.gateway import (
_is_service_installed,
_is_service_running,
supports_systemd_services,
@ -2398,7 +2398,7 @@ def setup_gateway(config: dict):
print_info(" Or as a boot-time service: sudo hermes gateway install --system")
print_info(" Or run in foreground: hermes gateway")
else:
from hermes_constants import is_container
from hermes_agent.constants import is_container
if is_container():
print_info("Start the gateway to bring your bots online:")
print_info(" hermes gateway run # Run as container main process")
@ -2428,7 +2428,7 @@ def setup_tools(config: dict, first_install: bool = False):
first_install: When True, uses the simplified first-install flow
(no platform menu, prompts for all unconfigured API keys).
"""
from hermes_cli.tools_config import tools_command
from hermes_agent.cli.tools_config import tools_command
tools_command(first_install=first_install, config=config)
@ -2450,14 +2450,14 @@ def _model_section_has_credentials(config: dict) -> bool:
``OPENAI_API_KEY`` / ``OPENROUTER_API_KEY`` values through OpenRouter.
"""
try:
from hermes_cli.auth import get_active_provider
from hermes_agent.cli.auth.auth import get_active_provider
if get_active_provider():
return True
except Exception:
pass
try:
from hermes_cli.auth import PROVIDER_REGISTRY
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
except Exception:
PROVIDER_REGISTRY = {} # type: ignore[assignment]
@ -2863,7 +2863,7 @@ def run_setup_wizard(args):
hermes setup tools just tool configuration
hermes setup agent just agent settings
"""
from hermes_cli.config import is_managed, managed_error
from hermes_agent.cli.config import is_managed, managed_error
if is_managed():
managed_error("run setup wizard")
return
@ -2918,7 +2918,7 @@ def run_setup_wizard(args):
return
# Check if this is an existing installation with a provider configured
from hermes_cli.auth import get_active_provider
from hermes_agent.cli.auth.auth import get_active_provider
active_provider = get_active_provider()
is_existing = (
@ -3072,8 +3072,8 @@ def _resolve_hermes_chat_argv() -> Optional[list[str]]:
return [hermes_bin, "chat"]
try:
if importlib.util.find_spec("hermes_cli") is not None:
return [sys.executable, "-m", "hermes_cli.main", "chat"]
if importlib.util.find_spec("hermes_agent.cli") is not None:
return [sys.executable, "-m", "hermes_agent.cli.main", "chat"]
except Exception:
pass
@ -3140,7 +3140,7 @@ def _run_first_time_quick_setup(config: dict, hermes_home, is_existing: bool):
def _run_quick_setup(config: dict, hermes_home):
"""Quick setup — only configure items that are missing."""
from hermes_cli.config import (
from hermes_agent.cli.config import (
get_missing_env_vars,
get_missing_config_fields,
check_config_version,

View file

@ -13,9 +13,9 @@ Config stored in ~/.hermes/config.yaml under:
"""
from typing import List, Optional, Set
from hermes_cli.config import load_config, save_config
from hermes_cli.colors import Colors, color
from hermes_cli.platforms import PLATFORMS as _PLATFORMS
from hermes_agent.cli.config import load_config, save_config
from hermes_agent.cli.ui.colors import Colors, color
from hermes_agent.cli.platforms import PLATFORMS as _PLATFORMS
# Backward-compatible view: {key: label_string} so existing code that
# iterates ``PLATFORMS.items()`` or calls ``PLATFORMS.get(key)`` keeps
@ -52,7 +52,7 @@ def save_disabled_skills(config: dict, disabled: Set[str], platform: Optional[st
def _list_all_skills() -> List[dict]:
"""Return all installed skills (ignoring disabled state)."""
try:
from tools.skills_tool import _find_all_skills
from hermes_agent.tools.skills.tool import _find_all_skills
return _find_all_skills(skip_disabled=True)
except Exception:
return []
@ -93,7 +93,7 @@ def _select_platform() -> Optional[str]:
def _toggle_by_category(skills: List[dict], disabled: Set[str]) -> Set[str]:
"""Toggle all skills in a category at once."""
from hermes_cli.curses_ui import curses_checklist
from hermes_agent.cli.ui.curses import curses_checklist
categories = _get_categories(skills)
cat_labels = []
@ -124,7 +124,7 @@ def _toggle_by_category(skills: List[dict], disabled: Set[str]) -> Set[str]:
def skills_command(args=None):
"""Entry point for `hermes skills`."""
from hermes_cli.curses_ui import curses_checklist
from hermes_agent.cli.ui.curses import curses_checklist
config = load_config()
skills = _list_all_skills()

View file

@ -21,7 +21,7 @@ from rich.table import Table
# Lazy imports to avoid circular dependencies and slow startup.
# tools.skills_hub and tools.skills_guard are imported inside functions.
from hermes_constants import display_hermes_home
from hermes_agent.constants import display_hermes_home
_console = Console()
@ -37,7 +37,7 @@ def _resolve_short_name(name: str, sources, console: Console) -> str:
matches exist, shows them and asks the user to use the full identifier.
Returns empty string if nothing found or ambiguous.
"""
from tools.skills_hub import unified_search
from hermes_agent.tools.skills.hub import unified_search
c = console or _console
c.print(f"[dim]Resolving '{name}'...[/]")
@ -144,7 +144,7 @@ def _derive_category_from_install_path(install_path: str) -> str:
def do_search(query: str, source: str = "all", limit: int = 10,
console: Optional[Console] = None) -> None:
"""Search registries and display results as a Rich table."""
from tools.skills_hub import GitHubAuth, create_source_router, unified_search
from hermes_agent.tools.skills.hub import GitHubAuth, create_source_router, unified_search
c = console or _console
c.print(f"\n[bold]Searching for:[/] {query}")
@ -187,7 +187,7 @@ def do_browse(page: int = 1, page_size: int = 20, source: str = "all",
Official skills are always shown first, regardless of source filter.
"""
from tools.skills_hub import (
from hermes_agent.tools.skills.hub import (
GitHubAuth, create_source_router, parallel_search_sources,
)
@ -311,11 +311,11 @@ def do_install(identifier: str, category: str = "", force: bool = False,
console: Optional[Console] = None, skip_confirm: bool = False,
invalidate_cache: bool = True) -> None:
"""Fetch, quarantine, scan, confirm, and install a skill."""
from tools.skills_hub import (
from hermes_agent.tools.skills.hub import (
GitHubAuth, create_source_router, ensure_hub_dirs,
quarantine_bundle, install_from_quarantine, HubLockFile,
)
from tools.skills_guard import scan_skill, should_allow_install, format_scan_report
from hermes_agent.tools.skills.guard import scan_skill, should_allow_install, format_scan_report
c = console or _console
ensure_hub_dirs()
@ -377,7 +377,7 @@ def do_install(identifier: str, category: str = "", force: bool = False,
q_path = quarantine_bundle(bundle)
except ValueError as exc:
c.print(f"[bold red]Installation blocked:[/] {exc}\n")
from tools.skills_hub import append_audit_log
from hermes_agent.tools.skills.hub import append_audit_log
append_audit_log("BLOCKED", bundle.name, bundle.source,
bundle.trust_level, "invalid_path", str(exc))
return
@ -395,7 +395,7 @@ def do_install(identifier: str, category: str = "", force: bool = False,
c.print(f"\n[bold red]Installation blocked:[/] {reason}")
# Clean up quarantine
shutil.rmtree(q_path, ignore_errors=True)
from tools.skills_hub import append_audit_log
from hermes_agent.tools.skills.hub import append_audit_log
append_audit_log("BLOCKED", bundle.name, bundle.source,
bundle.trust_level, result.verdict,
f"{len(result.findings)}_findings")
@ -445,18 +445,18 @@ def do_install(identifier: str, category: str = "", force: bool = False,
except ValueError as exc:
c.print(f"[bold red]Installation blocked:[/] {exc}\n")
shutil.rmtree(q_path, ignore_errors=True)
from tools.skills_hub import append_audit_log
from hermes_agent.tools.skills.hub import append_audit_log
append_audit_log("BLOCKED", bundle.name, bundle.source,
bundle.trust_level, "invalid_path", str(exc))
return
from tools.skills_hub import SKILLS_DIR
from hermes_agent.tools.skills.hub import SKILLS_DIR
c.print(f"[bold green]Installed:[/] {install_dir.relative_to(SKILLS_DIR)}")
c.print(f"[dim]Files: {', '.join(bundle.files.keys())}[/]\n")
if invalidate_cache:
# Invalidate the skills prompt cache so the new skill appears immediately
try:
from agent.prompt_builder import clear_skills_system_prompt_cache
from hermes_agent.agent.prompt_builder import clear_skills_system_prompt_cache
clear_skills_system_prompt_cache(clear_snapshot=True)
except Exception:
pass
@ -467,7 +467,7 @@ def do_install(identifier: str, category: str = "", force: bool = False,
def do_inspect(identifier: str, console: Optional[Console] = None) -> None:
"""Preview a skill's SKILL.md content without installing."""
from tools.skills_hub import GitHubAuth, create_source_router
from hermes_agent.tools.skills.hub import GitHubAuth, create_source_router
c = console or _console
auth = GitHubAuth()
@ -520,7 +520,7 @@ def browse_skills(page: int = 1, page_size: int = 20, source: str = "all") -> di
Returns ``{"items": [...], "page": int, "total_pages": int, "total": int}``.
"""
from tools.skills_hub import GitHubAuth, create_source_router
from hermes_agent.tools.skills.hub import GitHubAuth, create_source_router
page_size = max(1, min(page_size, 100))
_TRUST_RANK = {"builtin": 3, "trusted": 2, "community": 1}
@ -563,7 +563,7 @@ def browse_skills(page: int = 1, page_size: int = 20, source: str = "all") -> di
def inspect_skill(identifier: str) -> Optional[dict]:
"""Skill metadata (+ SKILL.md preview) for programmatic callers."""
from tools.skills_hub import GitHubAuth, create_source_router
from hermes_agent.tools.skills.hub import GitHubAuth, create_source_router
class _Q:
def print(self, *a, **k):
@ -601,9 +601,9 @@ def inspect_skill(identifier: str) -> Optional[dict]:
def do_list(source_filter: str = "all", console: Optional[Console] = None) -> None:
"""List installed skills, distinguishing hub, builtin, and local skills."""
from tools.skills_hub import HubLockFile, ensure_hub_dirs
from tools.skills_sync import _read_manifest
from tools.skills_tool import _find_all_skills
from hermes_agent.tools.skills.hub import HubLockFile, ensure_hub_dirs
from hermes_agent.tools.skills.sync import _read_manifest
from hermes_agent.tools.skills.tool import _find_all_skills
c = console or _console
ensure_hub_dirs()
@ -659,7 +659,7 @@ def do_list(source_filter: str = "all", console: Optional[Console] = None) -> No
def do_check(name: Optional[str] = None, console: Optional[Console] = None) -> None:
"""Check hub-installed skills for upstream updates."""
from tools.skills_hub import check_for_skill_updates
from hermes_agent.tools.skills.hub import check_for_skill_updates
c = console or _console
results = check_for_skill_updates(name=name)
@ -682,7 +682,7 @@ def do_check(name: Optional[str] = None, console: Optional[Console] = None) -> N
def do_update(name: Optional[str] = None, console: Optional[Console] = None) -> None:
"""Update hub-installed skills with upstream changes."""
from tools.skills_hub import HubLockFile, check_for_skill_updates
from hermes_agent.tools.skills.hub import HubLockFile, check_for_skill_updates
c = console or _console
lock = HubLockFile()
@ -702,8 +702,8 @@ def do_update(name: Optional[str] = None, console: Optional[Console] = None) ->
def do_audit(name: Optional[str] = None, console: Optional[Console] = None) -> None:
"""Re-run security scan on installed hub skills."""
from tools.skills_hub import HubLockFile, SKILLS_DIR
from tools.skills_guard import scan_skill, format_scan_report
from hermes_agent.tools.skills.hub import HubLockFile, SKILLS_DIR
from hermes_agent.tools.skills.guard import scan_skill, format_scan_report
c = console or _console
lock = HubLockFile()
@ -737,7 +737,7 @@ def do_uninstall(name: str, console: Optional[Console] = None,
skip_confirm: bool = False,
invalidate_cache: bool = True) -> None:
"""Remove a hub-installed skill with confirmation."""
from tools.skills_hub import uninstall_skill
from hermes_agent.tools.skills.hub import uninstall_skill
c = console or _console
@ -757,7 +757,7 @@ def do_uninstall(name: str, console: Optional[Console] = None,
c.print(f"[bold green]{msg}[/]\n")
if invalidate_cache:
try:
from agent.prompt_builder import clear_skills_system_prompt_cache
from hermes_agent.agent.prompt_builder import clear_skills_system_prompt_cache
clear_skills_system_prompt_cache(clear_snapshot=True)
except Exception:
pass
@ -773,7 +773,7 @@ def do_reset(name: str, restore: bool = False,
skip_confirm: bool = False,
invalidate_cache: bool = True) -> None:
"""Reset a bundled skill's manifest tracking (+ optionally restore from bundled)."""
from tools.skills_sync import reset_bundled_skill
from hermes_agent.tools.skills.sync import reset_bundled_skill
c = console or _console
@ -804,7 +804,7 @@ def do_reset(name: str, restore: bool = False,
if invalidate_cache:
try:
from agent.prompt_builder import clear_skills_system_prompt_cache
from hermes_agent.agent.prompt_builder import clear_skills_system_prompt_cache
clear_skills_system_prompt_cache(clear_snapshot=True)
except Exception:
pass
@ -815,7 +815,7 @@ def do_reset(name: str, restore: bool = False,
def do_tap(action: str, repo: str = "", console: Optional[Console] = None) -> None:
"""Manage taps (custom GitHub repo sources)."""
from tools.skills_hub import TapsManager
from hermes_agent.tools.skills.hub import TapsManager
c = console or _console
mgr = TapsManager()
@ -859,8 +859,8 @@ def do_tap(action: str, repo: str = "", console: Optional[Console] = None) -> No
def do_publish(skill_path: str, target: str = "github", repo: str = "",
console: Optional[Console] = None) -> None:
"""Publish a local skill to a registry (GitHub PR or ClawHub submission)."""
from tools.skills_hub import GitHubAuth, SKILLS_DIR
from tools.skills_guard import scan_skill, format_scan_report
from hermes_agent.tools.skills.hub import GitHubAuth, SKILLS_DIR
from hermes_agent.tools.skills.guard import scan_skill, format_scan_report
c = console or _console
path = Path(skill_path)
@ -1024,7 +1024,7 @@ def _github_publish(skill_path: Path, skill_name: str, target_repo: str,
def do_snapshot_export(output_path: str, console: Optional[Console] = None) -> None:
"""Export current hub skill configuration to a portable JSON file."""
from tools.skills_hub import HubLockFile, TapsManager
from hermes_agent.tools.skills.hub import HubLockFile, TapsManager
c = console or _console
lock = HubLockFile()
@ -1065,7 +1065,7 @@ def do_snapshot_export(output_path: str, console: Optional[Console] = None) -> N
def do_snapshot_import(input_path: str, force: bool = False,
console: Optional[Console] = None) -> None:
"""Re-install skills from a snapshot file."""
from tools.skills_hub import TapsManager
from hermes_agent.tools.skills.hub import TapsManager
c = console or _console
inp = Path(input_path)

View file

@ -19,7 +19,7 @@ def get_provider_request_timeout(
return None
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
except ImportError:
return None
@ -48,7 +48,7 @@ def get_provider_stale_timeout(
return None
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
except ImportError:
return None

View file

@ -16,16 +16,16 @@ from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, TypedDict
from hermes_cli.config import (
from hermes_agent.cli.config import (
load_config, save_config, get_env_value, save_env_value,
)
from hermes_cli.colors import Colors, color
from hermes_cli.nous_subscription import (
from hermes_agent.cli.ui.colors import Colors, color
from hermes_agent.cli.nous_subscription import (
apply_nous_managed_defaults,
get_nous_subscription_features,
)
from tools.tool_backend_helpers import fal_key_is_configured, managed_nous_tools_enabled
from utils import base_url_hostname
from hermes_agent.tools.backend_helpers import fal_key_is_configured, managed_nous_tools_enabled
from hermes_agent.utils import base_url_hostname
logger = logging.getLogger(__name__)
@ -34,7 +34,7 @@ PROJECT_ROOT = Path(__file__).parent.parent.resolve()
# ─── UI Helpers (shared with setup.py) ────────────────────────────────────────
from hermes_cli.cli_output import ( # noqa: E402 — late import block
from hermes_agent.cli.ui.output import ( # noqa: E402 — late import block
print_error as _print_error,
print_info as _print_info,
print_success as _print_success,
@ -83,7 +83,7 @@ def _get_effective_configurable_toolsets():
"""
result = list(CONFIGURABLE_TOOLSETS)
try:
from hermes_cli.plugins import discover_plugins, get_plugin_toolsets
from hermes_agent.cli.plugins import discover_plugins, get_plugin_toolsets
discover_plugins() # idempotent — ensures plugins are loaded
result.extend(get_plugin_toolsets())
except Exception:
@ -94,7 +94,7 @@ def _get_effective_configurable_toolsets():
def _get_plugin_toolset_keys() -> set:
"""Return the set of toolset keys provided by plugins."""
try:
from hermes_cli.plugins import discover_plugins, get_plugin_toolsets
from hermes_agent.cli.plugins import discover_plugins, get_plugin_toolsets
discover_plugins() # idempotent — ensures plugins are loaded
return {ts_key for ts_key, _, _ in get_plugin_toolsets()}
except Exception:
@ -103,7 +103,7 @@ def _get_plugin_toolset_keys() -> set:
# Platform display config — derived from the canonical registry so every
# module shares the same data. Kept as dict-of-dicts for backward
# compatibility with existing ``PLATFORMS[key]["label"]`` access patterns.
from hermes_cli.platforms import PLATFORMS as _PLATFORMS_REGISTRY
from hermes_agent.cli.platforms import PLATFORMS as _PLATFORMS_REGISTRY
PLATFORMS = {
k: {"label": info.label, "default_toolset": info.default_toolset}
@ -404,7 +404,7 @@ def _run_post_setup(post_setup_key: str):
if result.returncode == 0:
_print_success(" Node.js dependencies installed")
else:
from hermes_constants import display_hermes_home
from hermes_agent.constants import display_hermes_home
_print_warning(f" npm install failed - run manually: cd {display_hermes_home()}/hermes-agent && npm install")
elif not node_modules.exists():
_print_warning(" Node.js not found - browser tools require: npm install (in hermes-agent directory)")
@ -549,7 +549,7 @@ def _get_platform_tools(
include_default_mcp_servers: bool = True,
) -> Set[str]:
"""Resolve which individual toolset names are enabled for a platform."""
from toolsets import resolve_toolset
from hermes_agent.tools.toolsets import resolve_toolset
platform_toolsets = config.get("platform_toolsets") or {}
toolset_names = platform_toolsets.get(platform)
@ -696,7 +696,7 @@ def _toolset_has_keys(ts_key: str, config: dict = None) -> bool:
if ts_key == "vision":
try:
from agent.auxiliary_client import resolve_vision_provider_client
from hermes_agent.providers.auxiliary import resolve_vision_provider_client
_provider, client, _model = resolve_vision_provider_client()
return client is not None
@ -731,7 +731,7 @@ def _toolset_has_keys(ts_key: str, config: dict = None) -> bool:
def _prompt_choice(question: str, choices: list, default: int = 0) -> int:
"""Single-select menu (arrow keys). Delegates to curses_radiolist."""
from hermes_cli.curses_ui import curses_radiolist
from hermes_agent.cli.ui.curses import curses_radiolist
return curses_radiolist(question, choices, selected=default, cancel_returns=default)
@ -765,8 +765,8 @@ def _estimate_tool_tokens() -> Dict[str, int]:
try:
# Trigger full tool discovery (imports all tool modules).
import model_tools # noqa: F401
from tools.registry import registry
import hermes_agent.tools.dispatch # noqa: F401
from hermes_agent.tools.registry import registry
except Exception:
logger.debug("Tool registry unavailable; skipping token estimation")
_tool_token_cache = {}
@ -786,8 +786,8 @@ def _estimate_tool_tokens() -> Dict[str, int]:
def _prompt_toolset_checklist(platform_label: str, enabled: Set[str]) -> Set[str]:
"""Multi-select checklist of toolsets. Returns set of selected toolset keys."""
from hermes_cli.curses_ui import curses_checklist
from toolsets import resolve_toolset
from hermes_agent.cli.ui.curses import curses_checklist
from hermes_agent.tools.toolsets import resolve_toolset
# Pre-compute per-tool token counts (cached after first call).
tool_tokens = _estimate_tool_tokens()
@ -862,8 +862,8 @@ def _plugin_image_gen_providers() -> list[dict]:
function surfaces it alongside OpenAI automatically.
"""
try:
from agent.image_gen_registry import list_providers
from hermes_cli.plugins import _ensure_plugins_discovered
from hermes_agent.agent.image_gen.registry import list_providers
from hermes_agent.cli.plugins import _ensure_plugins_discovered
_ensure_plugins_discovered()
providers = list_providers()
@ -933,8 +933,8 @@ def _toolset_needs_configuration_prompt(ts_key: str, config: dict) -> bool:
if fal_key_is_configured():
return False
try:
from agent.image_gen_registry import list_providers
from hermes_cli.plugins import _ensure_plugins_discovered
from hermes_agent.agent.image_gen.registry import list_providers
from hermes_agent.cli.plugins import _ensure_plugins_discovered
_ensure_plugins_discovered()
for provider in list_providers():
@ -1085,7 +1085,7 @@ class _ImagegenBackend(TypedDict):
def _fal_model_catalog() -> Tuple[Dict[str, Dict[str, Any]], str]:
"""Lazy-load the FAL model catalog from the tool module."""
from tools.image_generation_tool import FAL_MODELS, DEFAULT_MODEL
from hermes_agent.tools.media.image_gen import FAL_MODELS, DEFAULT_MODEL
return FAL_MODELS, DEFAULT_MODEL
@ -1179,8 +1179,8 @@ def _plugin_image_gen_catalog(plugin_name: str):
``({}, None)`` if the provider isn't registered or has no models.
"""
try:
from agent.image_gen_registry import get_provider
from hermes_cli.plugins import _ensure_plugins_discovered
from hermes_agent.agent.image_gen.registry import get_provider
from hermes_agent.cli.plugins import _ensure_plugins_discovered
_ensure_plugins_discovered()
provider = get_provider(plugin_name)
@ -1836,7 +1836,7 @@ def tools_command(args=None, first_install: bool = False, config: dict = None):
platform_choices[idx] = f"Configure {pinfo['label']} ({new_count}/{total} enabled)"
print()
from hermes_constants import display_hermes_home
from hermes_agent.constants import display_hermes_home
print(color(f" Tool configuration saved to {display_hermes_home()}/config.yaml", Colors.DIM))
print(color(" Changes take effect on next 'hermes' or gateway restart.", Colors.DIM))
print()
@ -1852,7 +1852,7 @@ def _configure_mcp_tools_interactive(config: dict):
a per-server curses checklist. Writes changes back as ``tools.exclude``
entries in config.yaml.
"""
from hermes_cli.curses_ui import curses_checklist
from hermes_agent.cli.ui.curses import curses_checklist
mcp_servers = config.get("mcp_servers") or {}
if not mcp_servers:
@ -1873,7 +1873,7 @@ def _configure_mcp_tools_interactive(config: dict):
print(color(f" Connecting to {len(enabled_names)} server(s): {', '.join(enabled_names)}", Colors.DIM))
try:
from tools.mcp_tool import probe_mcp_server_tools
from hermes_agent.tools.mcp.tool import probe_mcp_server_tools
server_tools = probe_mcp_server_tools()
except Exception as exc:
_print_error(f"Failed to probe MCP servers: {exc}")

View file

@ -10,7 +10,7 @@ import subprocess
import threading
import time
from pathlib import Path
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
from typing import Dict, List, Optional
from rich.console import Console
@ -45,7 +45,7 @@ def cprint(text: str):
def _skin_color(key: str, fallback: str) -> str:
"""Get a color from the active skin, or return fallback."""
try:
from hermes_cli.skin_engine import get_active_skin
from hermes_agent.cli.ui.skin_engine import get_active_skin
return get_active_skin().get_color(key, fallback)
except Exception:
return fallback
@ -54,7 +54,7 @@ def _skin_color(key: str, fallback: str) -> str:
def _skin_branding(key: str, fallback: str) -> str:
"""Get a branding string from the active skin, or return fallback."""
try:
from hermes_cli.skin_engine import get_active_skin
from hermes_agent.cli.ui.skin_engine import get_active_skin
return get_active_skin().get_branding(key, fallback)
except Exception:
return fallback
@ -64,7 +64,7 @@ def _skin_branding(key: str, fallback: str) -> str:
# ASCII Art & Branding
# =========================================================================
from hermes_cli import __version__ as VERSION, __release_date__ as RELEASE_DATE
from hermes_agent.cli import __version__ as VERSION, __release_date__ as RELEASE_DATE
HERMES_AGENT_LOGO = """[bold #FFD700]██╗ ██╗███████╗██████╗ ███╗ ███╗███████╗███████╗ █████╗ ██████╗ ███████╗███╗ ██╗████████╗[/]
[bold #FFD700]██║ ██║██╔════╝██╔══██╗████╗ ████║██╔════╝██╔════╝ ██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝[/]
@ -103,7 +103,7 @@ def get_available_skills() -> Dict[str, List[str]]:
user's ``skills.disabled`` config list.
"""
try:
from tools.skills_tool import _find_all_skills
from hermes_agent.tools.skills.tool import _find_all_skills
all_skills = _find_all_skills() # already filtered
except Exception:
return {}
@ -330,9 +330,9 @@ def build_welcome_banner(console: Console, model: str, cwd: str,
get_toolset_for_tool: Callable to map tool name -> toolset name.
context_length: Model's context window size in tokens.
"""
from model_tools import check_tool_availability, TOOLSET_REQUIREMENTS
from hermes_agent.tools.dispatch import check_tool_availability, TOOLSET_REQUIREMENTS
if get_toolset_for_tool is None:
from model_tools import get_toolset_for_tool
from hermes_agent.tools.dispatch import get_toolset_for_tool
tools = tools or []
enabled_toolsets = enabled_toolsets or []
@ -364,7 +364,7 @@ def build_welcome_banner(console: Console, model: str, cwd: str,
# Use skin's custom caduceus art if provided
try:
from hermes_cli.skin_engine import get_active_skin
from hermes_agent.cli.ui.skin_engine import get_active_skin
_bskin = get_active_skin()
_hero = _bskin.banner_hero if hasattr(_bskin, 'banner_hero') and _bskin.banner_hero else HERMES_CADUCEUS
except Exception:
@ -444,7 +444,7 @@ def build_welcome_banner(console: Console, model: str, cwd: str,
# MCP Servers section (only if configured)
try:
from tools.mcp_tool import get_mcp_status
from hermes_agent.tools.mcp.tool import get_mcp_status
mcp_status = get_mcp_status()
except Exception:
mcp_status = []
@ -491,7 +491,7 @@ def build_welcome_banner(console: Console, model: str, cwd: str,
summary_parts.append("/help for commands")
# Show active profile name when not 'default'
try:
from hermes_cli.profiles import get_active_profile_name
from hermes_agent.cli.profiles import get_active_profile_name
_profile_name = get_active_profile_name()
if _profile_name and _profile_name != "default":
right_lines.append(f"[bold {accent}]Profile:[/] [{text}]{_profile_name}[/]")
@ -504,7 +504,7 @@ def build_welcome_banner(console: Console, model: str, cwd: str,
try:
behind = get_update_result(timeout=0.5)
if behind and behind > 0:
from hermes_cli.config import recommended_update_command
from hermes_agent.cli.config import recommended_update_command
commits_word = "commit" if behind == 1 else "commits"
right_lines.append(
f"[bold yellow]⚠ {behind} {commits_word} behind[/]"

View file

@ -10,9 +10,9 @@ import queue
import time as _time
import getpass
from hermes_cli.banner import cprint, _DIM, _RST
from hermes_cli.config import save_env_value_secure
from hermes_constants import display_hermes_home
from hermes_agent.cli.ui.banner import cprint, _DIM, _RST
from hermes_agent.cli.config import save_env_value_secure
from hermes_agent.constants import display_hermes_home
def clarify_callback(cli, question, choices):
@ -21,7 +21,7 @@ def clarify_callback(cli, question, choices):
Sets up the interactive selection UI, then blocks until the user
responds. Returns the user's choice or a timeout message.
"""
from cli import CLI_CONFIG
from hermes_agent.cli.repl import CLI_CONFIG
timeout = CLI_CONFIG.get("clarify", {}).get("timeout", 120)
response_queue = queue.Queue()
@ -200,7 +200,7 @@ def approval_callback(cli, command: str, description: str) -> str:
lock = cli._approval_lock
with lock:
from cli import CLI_CONFIG
from hermes_agent.cli.repl import CLI_CONFIG
timeout = CLI_CONFIG.get("approvals", {}).get("timeout", 60)
response_queue = queue.Queue()
choices = ["once", "session", "always", "deny"]

View file

@ -7,7 +7,7 @@ text-based numbered fallback for terminals without curses support.
import sys
from typing import Callable, List, Optional, Set
from hermes_cli.colors import Colors, color
from hermes_agent.cli.ui.colors import Colors, color
def flush_stdin() -> None:

View file

@ -7,7 +7,7 @@ mcp_config.py, and memory_setup.py.
import getpass
from hermes_cli.colors import Colors, color
from hermes_agent.cli.ui.colors import Colors, color
# ─── Print Helpers ────────────────────────────────────────────────────────────

View file

@ -77,7 +77,7 @@ USAGE
.. code-block:: python
from hermes_cli.skin_engine import get_active_skin, list_skins, set_active_skin
from hermes_agent.cli.ui.skin_engine import get_active_skin, list_skins, set_active_skin
skin = get_active_skin()
print(skin.colors["banner_title"]) # "#FFD700"
@ -108,7 +108,7 @@ from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
logger = logging.getLogger(__name__)

View file

@ -11,14 +11,14 @@ from pathlib import Path
PROJECT_ROOT = Path(__file__).parent.parent.resolve()
from hermes_cli.auth import AuthError, resolve_provider
from hermes_cli.colors import Colors, color
from hermes_cli.config import get_env_path, get_env_value, get_hermes_home, load_config
from hermes_cli.models import provider_label
from hermes_cli.nous_subscription import get_nous_subscription_features
from hermes_cli.runtime_provider import resolve_requested_provider
from hermes_constants import OPENROUTER_MODELS_URL
from tools.tool_backend_helpers import managed_nous_tools_enabled
from hermes_agent.cli.auth.auth import AuthError, resolve_provider
from hermes_agent.cli.ui.colors import Colors, color
from hermes_agent.cli.config import get_env_path, get_env_value, get_hermes_home, load_config
from hermes_agent.cli.models.models import provider_label
from hermes_agent.cli.nous_subscription import get_nous_subscription_features
from hermes_agent.cli.runtime_provider import resolve_requested_provider
from hermes_agent.constants import OPENROUTER_MODELS_URL
from hermes_agent.tools.backend_helpers import managed_nous_tools_enabled
def check_mark(ok: bool) -> str:
if ok:
@ -79,7 +79,7 @@ def _effective_provider_label() -> str:
return provider_label(effective)
from hermes_constants import is_termux as _is_termux
from hermes_agent.constants import is_termux as _is_termux
def show_status(args):
@ -141,7 +141,7 @@ def show_status(args):
display = redact_key(value) if not show_all else value
print(f" {name:<12} {check_mark(has_key)} {display}")
from hermes_cli.auth import get_anthropic_key
from hermes_agent.cli.auth.auth import get_anthropic_key
anthropic_value = get_anthropic_key()
anthropic_display = redact_key(anthropic_value) if not show_all else anthropic_value
print(f" {'Anthropic':<12} {check_mark(bool(anthropic_value))} {anthropic_display}")
@ -153,7 +153,7 @@ def show_status(args):
print(color("◆ Auth Providers", Colors.CYAN, Colors.BOLD))
try:
from hermes_cli.auth import get_nous_auth_status, get_codex_auth_status, get_qwen_auth_status
from hermes_agent.cli.auth.auth import get_nous_auth_status, get_codex_auth_status, get_qwen_auth_status
nous_status = get_nous_auth_status()
codex_status = get_codex_auth_status()
qwen_status = get_qwen_auth_status()
@ -344,7 +344,7 @@ def show_status(args):
print(color("◆ Gateway Service", Colors.CYAN, Colors.BOLD))
try:
from hermes_cli.gateway import get_gateway_runtime_snapshot, _format_gateway_pids
from hermes_agent.cli.gateway import get_gateway_runtime_snapshot, _format_gateway_pids
snapshot = get_gateway_runtime_snapshot()
is_running = snapshot.running

View file

@ -11,9 +11,9 @@ import shutil
import subprocess
from pathlib import Path
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
from hermes_cli.colors import Colors, color
from hermes_agent.cli.ui.colors import Colors, color
def log_info(msg: str):
print(f"{color('', Colors.CYAN)} {msg}")
@ -108,7 +108,7 @@ def remove_wrapper_script():
try:
# Check if it's our wrapper (contains hermes_cli reference)
content = wrapper.read_text()
if 'hermes_cli' in content or 'hermes-agent' in content:
if 'hermes_agent.cli' in content or 'hermes-agent' in content:
wrapper.unlink()
removed.append(wrapper)
except Exception as e:
@ -132,7 +132,7 @@ def uninstall_gateway_service():
# 1. Kill any standalone gateway processes (all platforms, including Termux)
try:
from hermes_cli.gateway import kill_gateway_processes, find_gateway_pids
from hermes_agent.cli.gateway import kill_gateway_processes, find_gateway_pids
pids = find_gateway_pids()
if pids:
killed = kill_gateway_processes()
@ -153,7 +153,7 @@ def uninstall_gateway_service():
# 2. Linux: uninstall systemd services (both user and system scopes)
if system == "Linux":
try:
from hermes_cli.gateway import (
from hermes_agent.cli.gateway import (
get_systemd_unit_path,
get_service_name,
_systemctl_cmd,
@ -190,7 +190,7 @@ def uninstall_gateway_service():
# 3. macOS: uninstall launchd plist
elif system == "Darwin":
try:
from hermes_cli.gateway import get_launchd_plist_path
from hermes_agent.cli.gateway import get_launchd_plist_path
plist_path = get_launchd_plist_path()
if plist_path.exists():
subprocess.run(["launchctl", "unload", str(plist_path)],
@ -207,7 +207,7 @@ def uninstall_gateway_service():
def _is_default_hermes_home(hermes_home: Path) -> bool:
"""Return True when ``hermes_home`` points at the default (non-profile) root."""
try:
from hermes_constants import get_default_hermes_root
from hermes_agent.constants import get_default_hermes_root
return hermes_home.resolve() == get_default_hermes_root().resolve()
except Exception:
return False
@ -218,7 +218,7 @@ def _discover_named_profiles():
if profile support is unavailable or nothing is installed beyond the
default root."""
try:
from hermes_cli.profiles import list_profiles
from hermes_agent.cli.profiles import list_profiles
except Exception:
return []
try:
@ -243,9 +243,9 @@ def _uninstall_profile(profile) -> None:
log_info(f"Uninstalling profile '{name}'...")
# 1. Stop and remove this profile's gateway service.
# Use `python -m hermes_cli.main` so we don't depend on a `hermes`
# Use `python -m hermes_agent.cli.main` so we don't depend on a `hermes`
# wrapper that may be half-removed mid-uninstall.
hermes_invocation = [_sys.executable, "-m", "hermes_cli.main", "--profile", name]
hermes_invocation = [_sys.executable, "-m", "hermes_agent.cli.main", "--profile", name]
for subcmd in ("stop", "uninstall"):
try:
subprocess.run(

View file

@ -5,8 +5,8 @@ Provides a FastAPI backend serving the Vite/React frontend and REST API
endpoints for managing configuration, environment variables, and sessions.
Usage:
python -m hermes_cli.main web # Start on http://127.0.0.1:9119
python -m hermes_cli.main web --port 8080
python -m hermes_agent.cli.main web # Start on http://127.0.0.1:9119
python -m hermes_agent.cli.main web --port 8080
"""
import asyncio
@ -28,11 +28,9 @@ from typing import Any, Dict, List, Optional
import yaml
PROJECT_ROOT = Path(__file__).parent.parent.resolve()
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
from hermes_cli import __version__, __release_date__
from hermes_cli.config import (
from hermes_agent.cli import __version__, __release_date__
from hermes_agent.cli.config import (
DEFAULT_CONFIG,
OPTIONAL_ENV_VARS,
get_config_path,
@ -46,7 +44,7 @@ from hermes_cli.config import (
check_config_version,
redact_key,
)
from gateway.status import get_running_pid, read_runtime_status
from hermes_agent.gateway.status import get_running_pid, read_runtime_status
try:
from fastapi import FastAPI, HTTPException, Request
@ -296,7 +294,7 @@ _SCHEMA_OVERRIDES: Dict[str, Dict[str, Any]] = {
"description": "Log level for agent.log",
"options": ["DEBUG", "INFO", "WARNING", "ERROR"],
},
"agent.service_tier": {
"hermes_agent.agent.service_tier": {
"type": "select",
"description": "API service tier (OpenAI/Anthropic)",
"options": ["", "auto", "default", "flex"],
@ -485,7 +483,7 @@ async def get_status():
gateway_updated_at = None
configured_gateway_platforms: set[str] | None = None
try:
from gateway.config import load_gateway_config
from hermes_agent.gateway.config import load_gateway_config
gateway_config = load_gateway_config()
configured_gateway_platforms = {
@ -528,7 +526,7 @@ async def get_status():
active_sessions = 0
try:
from hermes_state import SessionDB
from hermes_agent.state import SessionDB
db = SessionDB()
try:
sessions = db.list_sessions_rich(limit=50)
@ -588,7 +586,7 @@ _ACTION_PROCS: Dict[str, subprocess.Popen] = {}
def _spawn_hermes_action(subcommand: List[str], name: str) -> subprocess.Popen:
"""Spawn ``hermes <subcommand>`` detached and record the Popen handle.
Uses the running interpreter's ``hermes_cli.main`` module so the action
Uses the running interpreter's ``hermes_agent.cli.main`` module so the action
inherits the same venv/PYTHONPATH the web server is using.
"""
log_file_name = _ACTION_LOG_FILES[name]
@ -599,7 +597,7 @@ def _spawn_hermes_action(subcommand: List[str], name: str) -> subprocess.Popen:
f"\n=== {name} started {time.strftime('%Y-%m-%d %H:%M:%S')} ===\n".encode()
)
cmd = [sys.executable, "-m", "hermes_cli.main", *subcommand]
cmd = [sys.executable, "-m", "hermes_agent.cli.main", *subcommand]
popen_kwargs: Dict[str, Any] = {
"cwd": str(PROJECT_ROOT),
@ -697,7 +695,7 @@ async def get_action_status(name: str, lines: int = 200):
@app.get("/api/sessions")
async def get_sessions(limit: int = 20, offset: int = 0):
try:
from hermes_state import SessionDB
from hermes_agent.state import SessionDB
db = SessionDB()
try:
sessions = db.list_sessions_rich(limit=limit, offset=offset)
@ -722,7 +720,7 @@ async def search_sessions(q: str = "", limit: int = 20):
if not q or not q.strip():
return {"results": []}
try:
from hermes_state import SessionDB
from hermes_agent.state import SessionDB
db = SessionDB()
try:
# Auto-add prefix wildcards so partial words match
@ -838,7 +836,7 @@ def get_model_info():
# Resolve auto-detected context length (pass config_ctx=None to get
# purely auto-detected value, then separately report the override)
try:
from agent.model_metadata import get_model_context_length
from hermes_agent.providers.metadata import get_model_context_length
auto_ctx = get_model_context_length(
model=model_name,
base_url=base_url,
@ -858,7 +856,7 @@ def get_model_info():
# Try to get model capabilities from models.dev
caps = {}
try:
from agent.models_dev import get_model_capabilities
from hermes_agent.providers.metadata_dev import get_model_capabilities
mc = get_model_capabilities(provider=provider, model=model_name)
if mc is not None:
caps = {
@ -1062,7 +1060,7 @@ def _anthropic_oauth_status() -> Dict[str, Any]:
The dashboard reports the highest-priority source that's actually present.
"""
try:
from agent.anthropic_adapter import (
from hermes_agent.providers.anthropic_adapter import (
read_hermes_oauth_credentials,
read_claude_code_credentials,
_HERMES_OAUTH_FILE,
@ -1125,7 +1123,7 @@ def _claude_code_only_status() -> Dict[str, Any]:
when they also have a separate Hermes-managed PKCE login.
"""
try:
from agent.anthropic_adapter import read_claude_code_credentials
from hermes_agent.providers.anthropic_adapter import read_claude_code_credentials
creds = read_claude_code_credentials()
except Exception:
creds = None
@ -1200,7 +1198,7 @@ def _resolve_provider_status(provider_id: str, status_fn) -> Dict[str, Any]:
except Exception as e:
return {"logged_in": False, "error": str(e)}
try:
from hermes_cli import auth as hauth
from hermes_agent.cli import auth as hauth
if provider_id == "nous":
raw = hauth.get_nous_auth_status()
return {
@ -1288,14 +1286,14 @@ async def disconnect_oauth_provider(provider_id: str, request: Request):
# want to undo a disconnect.
if provider_id in ("anthropic", "claude-code"):
try:
from agent.anthropic_adapter import _HERMES_OAUTH_FILE
from hermes_agent.providers.anthropic_adapter import _HERMES_OAUTH_FILE
if _HERMES_OAUTH_FILE.exists():
_HERMES_OAUTH_FILE.unlink()
except Exception:
pass
# Also clear the credential pool entry if present.
try:
from hermes_cli.auth import clear_provider_auth
from hermes_agent.cli.auth.auth import clear_provider_auth
clear_provider_auth("anthropic")
except Exception:
pass
@ -1303,7 +1301,7 @@ async def disconnect_oauth_provider(provider_id: str, request: Request):
return {"ok": True, "provider": provider_id}
try:
from hermes_cli.auth import clear_provider_auth
from hermes_agent.cli.auth.auth import clear_provider_auth
cleared = clear_provider_auth(provider_id)
_log.info("oauth/disconnect: %s (cleared=%s)", provider_id, cleared)
return {"ok": bool(cleared), "provider": provider_id}
@ -1356,7 +1354,7 @@ _oauth_sessions_lock = threading.Lock()
# Guarded so hermes web still starts if anthropic_adapter is unavailable;
# Phase 2 endpoints will return 501 in that case.
try:
from agent.anthropic_adapter import (
from hermes_agent.providers.anthropic_adapter import (
_OAUTH_CLIENT_ID as _ANTHROPIC_OAUTH_CLIENT_ID,
_OAUTH_TOKEN_URL as _ANTHROPIC_OAUTH_TOKEN_URL,
_OAUTH_REDIRECT_URI as _ANTHROPIC_OAUTH_REDIRECT_URI,
@ -1400,7 +1398,7 @@ def _save_anthropic_oauth_creds(access_token: str, refresh_token: str, expires_a
Mirrors what auth_commands.add_command does so the dashboard flow leaves
the system in the same state as ``hermes auth add anthropic``.
"""
from agent.anthropic_adapter import _HERMES_OAUTH_FILE
from hermes_agent.providers.anthropic_adapter import _HERMES_OAUTH_FILE
payload = {
"accessToken": access_token,
"refreshToken": refresh_token,
@ -1412,7 +1410,7 @@ def _save_anthropic_oauth_creds(access_token: str, refresh_token: str, expires_a
# the file write — pool registration only matters for the rotation
# strategy, not for runtime credential resolution.
try:
from agent.credential_pool import (
from hermes_agent.providers.credential_pool import (
PooledCredential,
load_pool,
AUTH_TYPE_OAUTH,
@ -1539,9 +1537,9 @@ async def _start_device_code_flow(provider_id: str) -> Dict[str, Any]:
then spawns a background poller. Returns the user-facing display fields
so the UI can render the verification page link + user code.
"""
from hermes_cli import auth as hauth
from hermes_agent.cli import auth as hauth
if provider_id == "nous":
from hermes_cli.auth import _request_device_code, PROVIDER_REGISTRY
from hermes_agent.cli.auth.auth import _request_device_code, PROVIDER_REGISTRY
import httpx
pconfig = PROVIDER_REGISTRY["nous"]
portal_base_url = (
@ -1618,7 +1616,7 @@ async def _start_device_code_flow(provider_id: str) -> Dict[str, Any]:
def _nous_poller(session_id: str) -> None:
"""Background poller that drives a Nous device-code flow to completion."""
from hermes_cli.auth import _poll_for_token, refresh_nous_oauth_from_state
from hermes_agent.cli.auth.auth import _poll_for_token, refresh_nous_oauth_from_state
from datetime import datetime, timezone
import httpx
with _oauth_sessions_lock:
@ -1662,7 +1660,7 @@ def _nous_poller(session_id: str) -> None:
auth_state, min_key_ttl_seconds=300, timeout_seconds=15.0,
force_refresh=False, force_mint=True,
)
from hermes_cli.auth import persist_nous_credentials
from hermes_agent.cli.auth.auth import persist_nous_credentials
persist_nous_credentials(full_state)
with _oauth_sessions_lock:
sess["status"] = "approved"
@ -1691,7 +1689,7 @@ def _codex_full_login_worker(session_id: str) -> None:
"""
try:
import httpx
from hermes_cli.auth import (
from hermes_agent.cli.auth.auth import (
CODEX_OAUTH_CLIENT_ID,
CODEX_OAUTH_TOKEN_URL,
DEFAULT_CODEX_BASE_URL,
@ -1775,7 +1773,7 @@ def _codex_full_login_worker(session_id: str) -> None:
raise RuntimeError("token exchange did not return access_token")
# Persist via credential pool — same shape as auth_commands.add_command
from agent.credential_pool import (
from hermes_agent.providers.credential_pool import (
PooledCredential,
load_pool,
AUTH_TYPE_OAUTH,
@ -1889,7 +1887,7 @@ async def cancel_oauth_session(session_id: str, request: Request):
@app.get("/api/sessions/{session_id}")
async def get_session_detail(session_id: str):
from hermes_state import SessionDB
from hermes_agent.state import SessionDB
db = SessionDB()
try:
sid = db.resolve_session_id(session_id)
@ -1903,7 +1901,7 @@ async def get_session_detail(session_id: str):
@app.get("/api/sessions/{session_id}/messages")
async def get_session_messages(session_id: str):
from hermes_state import SessionDB
from hermes_agent.state import SessionDB
db = SessionDB()
try:
sid = db.resolve_session_id(session_id)
@ -1917,7 +1915,7 @@ async def get_session_messages(session_id: str):
@app.delete("/api/sessions/{session_id}")
async def delete_session_endpoint(session_id: str):
from hermes_state import SessionDB
from hermes_agent.state import SessionDB
db = SessionDB()
try:
if not db.delete_session(session_id):
@ -1940,7 +1938,7 @@ async def get_logs(
component: Optional[str] = None,
search: Optional[str] = None,
):
from hermes_cli.logs import _read_tail, LOG_FILES
from hermes_agent.cli.logs import _read_tail, LOG_FILES
log_name = LOG_FILES.get(file)
if not log_name:
@ -1950,7 +1948,7 @@ async def get_logs(
return {"file": file, "lines": []}
try:
from hermes_logging import COMPONENT_PREFIXES
from hermes_agent.logging import COMPONENT_PREFIXES
except ImportError:
COMPONENT_PREFIXES = {}
@ -2003,13 +2001,13 @@ class CronJobUpdate(BaseModel):
@app.get("/api/cron/jobs")
async def list_cron_jobs():
from cron.jobs import list_jobs
from hermes_agent.cron.jobs import list_jobs
return list_jobs(include_disabled=True)
@app.get("/api/cron/jobs/{job_id}")
async def get_cron_job(job_id: str):
from cron.jobs import get_job
from hermes_agent.cron.jobs import get_job
job = get_job(job_id)
if not job:
raise HTTPException(status_code=404, detail="Job not found")
@ -2018,7 +2016,7 @@ async def get_cron_job(job_id: str):
@app.post("/api/cron/jobs")
async def create_cron_job(body: CronJobCreate):
from cron.jobs import create_job
from hermes_agent.cron.jobs import create_job
try:
job = create_job(prompt=body.prompt, schedule=body.schedule,
name=body.name, deliver=body.deliver)
@ -2030,7 +2028,7 @@ async def create_cron_job(body: CronJobCreate):
@app.put("/api/cron/jobs/{job_id}")
async def update_cron_job(job_id: str, body: CronJobUpdate):
from cron.jobs import update_job
from hermes_agent.cron.jobs import update_job
job = update_job(job_id, body.updates)
if not job:
raise HTTPException(status_code=404, detail="Job not found")
@ -2039,7 +2037,7 @@ async def update_cron_job(job_id: str, body: CronJobUpdate):
@app.post("/api/cron/jobs/{job_id}/pause")
async def pause_cron_job(job_id: str):
from cron.jobs import pause_job
from hermes_agent.cron.jobs import pause_job
job = pause_job(job_id)
if not job:
raise HTTPException(status_code=404, detail="Job not found")
@ -2048,7 +2046,7 @@ async def pause_cron_job(job_id: str):
@app.post("/api/cron/jobs/{job_id}/resume")
async def resume_cron_job(job_id: str):
from cron.jobs import resume_job
from hermes_agent.cron.jobs import resume_job
job = resume_job(job_id)
if not job:
raise HTTPException(status_code=404, detail="Job not found")
@ -2057,7 +2055,7 @@ async def resume_cron_job(job_id: str):
@app.post("/api/cron/jobs/{job_id}/trigger")
async def trigger_cron_job(job_id: str):
from cron.jobs import trigger_job
from hermes_agent.cron.jobs import trigger_job
job = trigger_job(job_id)
if not job:
raise HTTPException(status_code=404, detail="Job not found")
@ -2066,7 +2064,7 @@ async def trigger_cron_job(job_id: str):
@app.delete("/api/cron/jobs/{job_id}")
async def delete_cron_job(job_id: str):
from cron.jobs import remove_job
from hermes_agent.cron.jobs import remove_job
if not remove_job(job_id):
raise HTTPException(status_code=404, detail="Job not found")
return {"ok": True}
@ -2084,8 +2082,8 @@ class SkillToggle(BaseModel):
@app.get("/api/skills")
async def get_skills():
from tools.skills_tool import _find_all_skills
from hermes_cli.skills_config import get_disabled_skills
from hermes_agent.tools.skills.tool import _find_all_skills
from hermes_agent.cli.skills_config import get_disabled_skills
config = load_config()
disabled = get_disabled_skills(config)
skills = _find_all_skills(skip_disabled=True)
@ -2096,7 +2094,7 @@ async def get_skills():
@app.put("/api/skills/toggle")
async def toggle_skill(body: SkillToggle):
from hermes_cli.skills_config import get_disabled_skills, save_disabled_skills
from hermes_agent.cli.skills_config import get_disabled_skills, save_disabled_skills
config = load_config()
disabled = get_disabled_skills(config)
if body.enabled:
@ -2109,12 +2107,12 @@ async def toggle_skill(body: SkillToggle):
@app.get("/api/tools/toolsets")
async def get_toolsets():
from hermes_cli.tools_config import (
from hermes_agent.cli.tools_config import (
_get_effective_configurable_toolsets,
_get_platform_tools,
_toolset_has_keys,
)
from toolsets import resolve_toolset
from hermes_agent.tools.toolsets import resolve_toolset
config = load_config()
enabled_toolsets = _get_platform_tools(
@ -2175,8 +2173,8 @@ async def update_config_raw(body: RawConfigUpdate):
@app.get("/api/analytics/usage")
async def get_usage_analytics(days: int = 30):
from hermes_state import SessionDB
from agent.insights import InsightsEngine
from hermes_agent.state import SessionDB
from hermes_agent.agent.insights import InsightsEngine
db = SessionDB()
try:

View file

@ -18,14 +18,14 @@ import time
from pathlib import Path
from typing import Dict
from hermes_constants import display_hermes_home
from hermes_agent.constants import display_hermes_home
_SUBSCRIPTIONS_FILENAME = "webhook_subscriptions.json"
def _hermes_home() -> Path:
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
return get_hermes_home()
@ -58,7 +58,7 @@ def _save_subscriptions(subs: Dict[str, dict]) -> None:
def _get_webhook_config() -> dict:
"""Load webhook platform config. Returns {} if not configured."""
try:
from hermes_cli.config import load_config
from hermes_agent.cli.config import load_config
cfg = load_config()
return cfg.get("platforms", {}).get("webhook", {})
except Exception:

View file

@ -15,7 +15,7 @@ The gateway ticks the scheduler every 60 seconds. A file lock prevents
duplicate execution if multiple processes overlap.
"""
from cron.jobs import (
from hermes_agent.cron.jobs import (
create_job,
get_job,
list_jobs,
@ -26,7 +26,7 @@ from cron.jobs import (
trigger_job,
JOBS_FILE,
)
from cron.scheduler import tick
from hermes_agent.cron.scheduler import tick
__all__ = [
"create_job",

View file

@ -15,12 +15,12 @@ import re
import uuid
from datetime import datetime, timedelta
from pathlib import Path
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
from typing import Optional, Dict, List, Any
logger = logging.getLogger(__name__)
from hermes_time import now as _hermes_now
from hermes_agent.time import now as _hermes_now
try:
from croniter import croniter

View file

@ -29,14 +29,9 @@ except ImportError:
from pathlib import Path
from typing import List, Optional
# Add parent directory to path for imports BEFORE repo-level imports.
# Without this, standalone invocations (e.g. after `hermes update` reloads
# the module) fail with ModuleNotFoundError for hermes_time et al.
sys.path.insert(0, str(Path(__file__).parent.parent))
from hermes_constants import get_hermes_home
from hermes_cli.config import load_config
from hermes_time import now as _hermes_now
from hermes_agent.constants import get_hermes_home
from hermes_agent.cli.config import load_config
from hermes_agent.time import now as _hermes_now
logger = logging.getLogger(__name__)
@ -76,7 +71,7 @@ _LEGACY_HOME_TARGET_ENV_VARS = {
"QQBOT_HOME_CHANNEL": "QQ_HOME_CHANNEL",
}
from cron.jobs import get_due_jobs, mark_job_run, save_job_output, advance_next_run
from hermes_agent.cron.jobs import get_due_jobs, mark_job_run, save_job_output, advance_next_run
# Sentinel: when a cron agent has nothing new to report, it can start its
# response with this marker to suppress delivery. Output is still saved
@ -152,7 +147,7 @@ def _resolve_single_delivery_target(job: dict, deliver_value: str) -> Optional[d
platform_name, rest = deliver_value.split(":", 1)
platform_key = platform_name.lower()
from tools.send_message_tool import _parse_target_ref
from hermes_agent.tools.send_message import _parse_target_ref
parsed_chat_id, parsed_thread_id, is_explicit = _parse_target_ref(platform_key, rest)
if is_explicit:
@ -162,7 +157,7 @@ def _resolve_single_delivery_target(job: dict, deliver_value: str) -> Optional[d
# Resolve human-friendly labels like "Alice (dm)" to real IDs.
try:
from gateway.channel_directory import resolve_channel_name
from hermes_agent.gateway.channel_directory import resolve_channel_name
resolved = resolve_channel_name(platform_key, chat_id)
if resolved:
parsed_chat_id, parsed_thread_id, resolved_is_explicit = _parse_target_ref(platform_key, resolved)
@ -285,8 +280,8 @@ def _deliver_result(job: dict, content: str, adapters=None, loop=None) -> Option
return msg
return None # local-only jobs don't deliver — not a failure
from tools.send_message_tool import _send_to_platform
from gateway.config import load_gateway_config, Platform
from hermes_agent.tools.send_message import _send_to_platform
from hermes_agent.gateway.config import load_gateway_config, Platform
platform_map = {
"telegram": Platform.TELEGRAM,
@ -332,7 +327,7 @@ def _deliver_result(job: dict, content: str, adapters=None, loop=None) -> Option
delivery_content = content
# Extract MEDIA: tags so attachments are forwarded as files, not raw text
from gateway.platforms.base import BasePlatformAdapter
from hermes_agent.gateway.platforms.base import BasePlatformAdapter
media_files, cleaned_delivery_content = BasePlatformAdapter.extract_media(delivery_content)
try:
@ -508,7 +503,7 @@ def _run_job_script(script_path: str) -> tuple[bool, str]:
(success, output) on failure *output* contains the error message so the
LLM can report the problem to the user.
"""
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
scripts_dir = get_hermes_home() / "scripts"
scripts_dir.mkdir(parents=True, exist_ok=True)
@ -550,7 +545,7 @@ def _run_job_script(script_path: str) -> tuple[bool, str]:
# Redact secrets from both stdout and stderr before any return path.
try:
from agent.redact import redact_sensitive_text
from hermes_agent.agent.redact import redact_sensitive_text
stdout = redact_sensitive_text(stdout)
stderr = redact_sensitive_text(stderr)
except Exception:
@ -663,7 +658,7 @@ def _build_job_prompt(job: dict, prerun_script: Optional[tuple] = None) -> str:
if not skill_names:
return prompt
from tools.skills_tool import skill_view
from hermes_agent.tools.skills.tool import skill_view
parts = []
skipped: list[str] = []
@ -707,13 +702,13 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
Returns:
Tuple of (success, full_output_doc, final_response, error_message)
"""
from run_agent import AIAgent
from hermes_agent.agent.loop import AIAgent
# Initialize SQLite session store so cron job messages are persisted
# and discoverable via session_search (same pattern as gateway/run.py).
_session_db = None
try:
from hermes_state import SessionDB
from hermes_agent.state import SessionDB
_session_db = SessionDB()
except Exception as e:
logger.debug("Job '%s': SQLite session store not available: %s", job.get("id", "?"), e)
@ -757,7 +752,7 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
# Use ContextVars for per-job session/delivery state so parallel jobs
# don't clobber each other's targets (os.environ is process-global).
from gateway.session_context import set_session_vars, clear_session_vars, _VAR_MAP
from hermes_agent.gateway.session_context import set_session_vars, clear_session_vars, _VAR_MAP
_ctx_tokens = set_session_vars(
platform=origin["platform"] if origin else "",
@ -802,7 +797,7 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
# Apply IPv4 preference if configured.
try:
from hermes_constants import apply_ipv4_preference
from hermes_agent.constants import apply_ipv4_preference
_net_cfg = _cfg.get("network", {})
if isinstance(_net_cfg, dict) and _net_cfg.get("force_ipv4"):
apply_ipv4_preference(force=True)
@ -810,7 +805,7 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
pass
# Reasoning config from config.yaml
from hermes_constants import parse_reasoning_effort
from hermes_agent.constants import parse_reasoning_effort
effort = str(_cfg.get("agent", {}).get("reasoning_effort", "")).strip()
reasoning_config = parse_reasoning_effort(effort)
@ -837,7 +832,7 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
# Provider routing
pr = _cfg.get("provider_routing", {})
from hermes_cli.runtime_provider import (
from hermes_agent.cli.runtime_provider import (
resolve_runtime_provider,
format_runtime_provider_error,
)
@ -857,7 +852,7 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
runtime_provider = str(runtime.get("provider") or "").strip().lower()
if runtime_provider:
try:
from agent.credential_pool import load_pool
from hermes_agent.providers.credential_pool import load_pool
pool = load_pool(runtime_provider)
if pool.has_credentials():
credential_pool = pool

View file

@ -20,9 +20,9 @@ suppress delivery.
import logging
import threading
logger = logging.getLogger("hooks.boot-md")
logger = logging.getLogger(__name__)
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
HERMES_HOME = get_hermes_home()
BOOT_FILE = HERMES_HOME / "BOOT.md"
@ -45,7 +45,7 @@ def _build_boot_prompt(content: str) -> str:
def _run_boot_agent(content: str) -> None:
"""Spawn a one-shot agent session to execute the boot instructions."""
try:
from run_agent import AIAgent
from hermes_agent.agent.loop import AIAgent
prompt = _build_boot_prompt(content)
agent = AIAgent(

View file

@ -11,8 +11,8 @@ import logging
from datetime import datetime
from typing import Any, Dict, List, Optional
from hermes_cli.config import get_hermes_home
from utils import atomic_json_write
from hermes_agent.cli.config import get_hermes_home
from hermes_agent.utils import atomic_json_write
logger = logging.getLogger(__name__)
@ -63,7 +63,7 @@ def build_channel_directory(adapters: Dict[Any, Any]) -> Dict[str, Any]:
Returns the directory dict and writes it to DIRECTORY_PATH.
"""
from gateway.config import Platform
from hermes_agent.gateway.config import Platform
platforms: Dict[str, List[Dict[str, str]]] = {}
@ -144,7 +144,7 @@ def _build_slack(adapter) -> List[Dict[str, str]]:
return _build_from_sessions("slack")
try:
from tools.send_message_tool import _send_slack # noqa: F401
from hermes_agent.tools.send_message import _send_slack # noqa: F401
# Use the Slack Web API directly if available
except Exception:
pass

View file

@ -16,8 +16,8 @@ from dataclasses import dataclass, field
from typing import Dict, List, Optional, Any
from enum import Enum
from hermes_cli.config import get_hermes_home
from utils import is_truthy_value
from hermes_agent.cli.config import get_hermes_home
from hermes_agent.utils import is_truthy_value
logger = logging.getLogger(__name__)
@ -821,7 +821,7 @@ def _validate_gateway_config(config: "GatewayConfig") -> None:
# without changing placeholder values get a clear startup error instead
# of a confusing "auth failed" from the platform API.
try:
from hermes_cli.auth import has_usable_secret
from hermes_agent.cli.auth.auth import has_usable_secret
except ImportError:
has_usable_secret = None # type: ignore[assignment]

View file

@ -14,7 +14,7 @@ from datetime import datetime
from dataclasses import dataclass
from typing import Dict, List, Optional, Any
from hermes_cli.config import get_hermes_home
from hermes_agent.cli.config import get_hermes_home
logger = logging.getLogger(__name__)

View file

@ -25,7 +25,7 @@ from typing import Any, Callable, Dict, List, Optional
import yaml
from hermes_cli.config import get_hermes_home
from hermes_agent.cli.config import get_hermes_home
HOOKS_DIR = get_hermes_home() / "hooks"
@ -54,7 +54,7 @@ class HookRegistry:
def _register_builtin_hooks(self) -> None:
"""Register built-in hooks that are always active."""
try:
from gateway.builtin_hooks.boot_md import handle as boot_md_handle
from hermes_agent.gateway.builtin_hooks.boot_md import handle as boot_md_handle
self._handlers.setdefault("gateway:startup", []).append(boot_md_handle)
self._loaded_hooks.append({

View file

@ -14,7 +14,7 @@ import logging
from datetime import datetime
from typing import Optional
from hermes_cli.config import get_hermes_home
from hermes_agent.cli.config import get_hermes_home
logger = logging.getLogger(__name__)
@ -118,7 +118,7 @@ def _append_to_sqlite(session_id: str, message: dict) -> None:
"""Append a message to the SQLite session database."""
db = None
try:
from hermes_state import SessionDB
from hermes_agent.state import SessionDB
db = SessionDB()
db.append_message(
session_id=session_id,

View file

@ -27,7 +27,7 @@ import time
from pathlib import Path
from typing import Optional
from hermes_constants import get_hermes_dir
from hermes_agent.constants import get_hermes_dir
# Unambiguous alphabet -- excludes 0/O, 1/I to prevent confusion

View file

@ -33,8 +33,8 @@ import time
import uuid
from typing import Any, Dict, List, Optional
from aiohttp import web
from gateway.config import Platform, PlatformConfig
from gateway.platforms.base import (
from hermes_agent.gateway.config import Platform, PlatformConfig
from hermes_agent.gateway.platforms.base import (
BasePlatformAdapter,
SendResult,
is_network_accessible,
@ -279,7 +279,7 @@ class ResponseStore:
self._max_size = max_size
if db_path is None:
try:
from hermes_cli.config import get_hermes_home
from hermes_agent.cli.config import get_hermes_home
db_path = str(get_hermes_home() / "response_store.db")
except Exception:
db_path = ":memory:"
@ -513,7 +513,7 @@ def _derive_chat_session_id(
_CRON_AVAILABLE = False
try:
from cron.jobs import (
from hermes_agent.cron.jobs import (
list_jobs as _cron_list,
get_job as _cron_get,
create_job as _cron_create,
@ -592,7 +592,7 @@ class APIServerAdapter(BasePlatformAdapter):
if explicit and explicit.strip():
return explicit.strip()
try:
from hermes_cli.profiles import get_active_profile_name
from hermes_agent.cli.profiles import get_active_profile_name
profile = get_active_profile_name()
if profile and profile not in ("default", "custom"):
return profile
@ -668,7 +668,7 @@ class APIServerAdapter(BasePlatformAdapter):
"""
if self._session_db is None:
try:
from hermes_state import SessionDB
from hermes_agent.state import SessionDB
self._session_db = SessionDB()
except Exception as e:
logger.debug("SessionDB unavailable for API server: %s", e)
@ -695,9 +695,9 @@ class APIServerAdapter(BasePlatformAdapter):
from config.yaml platform_toolsets.api_server (same as all other
gateway platforms), falling back to the hermes-api-server default.
"""
from run_agent import AIAgent
from gateway.run import _resolve_runtime_agent_kwargs, _resolve_gateway_model, _load_gateway_config
from hermes_cli.tools_config import _get_platform_tools
from hermes_agent.agent.loop import AIAgent
from hermes_agent.gateway.run import _resolve_runtime_agent_kwargs, _resolve_gateway_model, _load_gateway_config
from hermes_agent.cli.tools_config import _get_platform_tools
runtime_kwargs = _resolve_runtime_agent_kwargs()
model = _resolve_gateway_model()
@ -709,7 +709,7 @@ class APIServerAdapter(BasePlatformAdapter):
# Load fallback provider chain so the API server platform has the
# same fallback behaviour as Telegram/Discord/Slack (fixes #4954).
from gateway.run import GatewayRunner
from hermes_agent.gateway.run import GatewayRunner
fallback_model = GatewayRunner._load_fallback_model()
agent = AIAgent(
@ -746,7 +746,7 @@ class APIServerAdapter(BasePlatformAdapter):
dashboard can display full status without needing a shared PID file or
/proc access. No authentication required.
"""
from gateway.status import read_runtime_status
from hermes_agent.gateway.status import read_runtime_status
runtime = read_runtime_status() or {}
return web.json_response({
@ -927,7 +927,7 @@ class APIServerAdapter(BasePlatformAdapter):
return
if name.startswith("_"):
return
from agent.display import get_tool_emoji
from hermes_agent.agent.display import get_tool_emoji
emoji = get_tool_emoji(name)
label = preview or name
_stream_q.put(("__tool_progress__", {
@ -2505,7 +2505,7 @@ class APIServerAdapter(BasePlatformAdapter):
# Ported from openclaw/openclaw#64586.
if is_network_accessible(self._host) and self._api_key:
try:
from hermes_cli.auth import has_usable_secret
from hermes_agent.cli.auth.auth import has_usable_secret
if not has_usable_secret(self._api_key, min_length=8):
logger.error(
"[%s] Refusing to start: API_SERVER_KEY is set to a "

View file

@ -19,7 +19,7 @@ import uuid
from abc import ABC, abstractmethod
from urllib.parse import urlsplit
from utils import normalize_proxy_url
from hermes_agent.utils import normalize_proxy_url
logger = logging.getLogger(__name__)
@ -235,12 +235,9 @@ from pathlib import Path
from typing import Dict, List, Optional, Any, Callable, Awaitable, Tuple
from enum import Enum
from pathlib import Path as _Path
sys.path.insert(0, str(_Path(__file__).resolve().parents[2]))
from gateway.config import Platform, PlatformConfig
from gateway.session import SessionSource, build_session_key
from hermes_constants import get_hermes_dir
from hermes_agent.gateway.config import Platform, PlatformConfig
from hermes_agent.gateway.session import SessionSource, build_session_key
from hermes_agent.constants import get_hermes_dir
GATEWAY_SECRET_CAPTURE_UNSUPPORTED_MESSAGE = (
@ -296,7 +293,7 @@ async def _ssrf_redirect_guard(response):
"""
if response.is_redirect and response.next_request:
redirect_url = str(response.next_request.url)
from tools.url_safety import is_safe_url
from hermes_agent.tools.security.urls import is_safe_url
if not is_safe_url(redirect_url):
raise ValueError(
f"Blocked redirect to private/internal address: {safe_url_for_log(redirect_url)}"
@ -385,7 +382,7 @@ async def cache_image_from_url(url: str, ext: str = ".jpg", retries: int = 2) ->
Raises:
ValueError: If the URL targets a private/internal network (SSRF protection).
"""
from tools.url_safety import is_safe_url
from hermes_agent.tools.security.urls import is_safe_url
if not is_safe_url(url):
raise ValueError(f"Blocked unsafe URL (SSRF protection): {safe_url_for_log(url)}")
@ -500,7 +497,7 @@ async def cache_audio_from_url(url: str, ext: str = ".ogg", retries: int = 2) ->
Raises:
ValueError: If the URL targets a private/internal network (SSRF protection).
"""
from tools.url_safety import is_safe_url
from hermes_agent.tools.security.urls import is_safe_url
if not is_safe_url(url):
raise ValueError(f"Blocked unsafe URL (SSRF protection): {safe_url_for_log(url)}")
@ -942,7 +939,7 @@ class BasePlatformAdapter(ABC):
self._fatal_error_message = None
self._fatal_error_retryable = True
try:
from gateway.status import write_runtime_status
from hermes_agent.gateway.status import write_runtime_status
write_runtime_status(platform=self.platform.value, platform_state="connected", error_code=None, error_message=None)
except Exception:
pass
@ -952,7 +949,7 @@ class BasePlatformAdapter(ABC):
if self.has_fatal_error:
return
try:
from gateway.status import write_runtime_status
from hermes_agent.gateway.status import write_runtime_status
write_runtime_status(platform=self.platform.value, platform_state="disconnected", error_code=None, error_message=None)
except Exception:
pass
@ -963,7 +960,7 @@ class BasePlatformAdapter(ABC):
self._fatal_error_message = message
self._fatal_error_retryable = retryable
try:
from gateway.status import write_runtime_status
from hermes_agent.gateway.status import write_runtime_status
write_runtime_status(
platform=self.platform.value,
platform_state="fatal",
@ -983,7 +980,7 @@ class BasePlatformAdapter(ABC):
def _acquire_platform_lock(self, scope: str, identity: str, resource_desc: str) -> bool:
"""Acquire a scoped lock for this adapter. Returns True on success."""
from gateway.status import acquire_scoped_lock
from hermes_agent.gateway.status import acquire_scoped_lock
self._platform_lock_scope = scope
self._platform_lock_identity = identity
acquired, existing = acquire_scoped_lock(
@ -1006,7 +1003,7 @@ class BasePlatformAdapter(ABC):
identity = getattr(self, '_platform_lock_identity', None)
if not identity:
return
from gateway.status import release_scoped_lock
from hermes_agent.gateway.status import release_scoped_lock
release_scoped_lock(self._platform_lock_scope, identity)
self._platform_lock_identity = None
@ -1705,7 +1702,7 @@ class BasePlatformAdapter(ABC):
# session lifecycle and its cleanup races with the running task
# (see PR #4926).
cmd = event.get_command()
from hermes_cli.commands import should_bypass_active_session
from hermes_agent.cli.commands import should_bypass_active_session
if should_bypass_active_session(cmd):
logger.debug(
@ -1879,7 +1876,7 @@ class BasePlatformAdapter(ABC):
and not media_files
and event.source.chat_id not in self._auto_tts_disabled_chats):
try:
from tools.tts_tool import text_to_speech_tool, check_tts_requirements
from hermes_agent.tools.media.tts import text_to_speech_tool, check_tts_requirements
if check_tts_requirements():
import json as _json
speech_text = re.sub(r'[*_`#\[\]()]', '', text_content)[:4000].strip()

View file

@ -20,8 +20,8 @@ from urllib.parse import quote
import httpx
from gateway.config import Platform, PlatformConfig
from gateway.platforms.base import (
from hermes_agent.gateway.config import Platform, PlatformConfig
from hermes_agent.gateway.platforms.base import (
BasePlatformAdapter,
MessageEvent,
MessageType,
@ -30,7 +30,7 @@ from gateway.platforms.base import (
cache_audio_from_bytes,
cache_document_from_bytes,
)
from gateway.platforms.helpers import strip_markdown
from hermes_agent.gateway.platforms.helpers import strip_markdown
logger = logging.getLogger(__name__)
@ -502,7 +502,7 @@ class BlueBubblesAdapter(BasePlatformAdapter):
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
try:
from gateway.platforms.base import cache_image_from_url
from hermes_agent.gateway.platforms.base import cache_image_from_url
local_path = await cache_image_from_url(image_url)
return await self._send_attachment(chat_id, local_path, caption=caption)

View file

@ -87,9 +87,9 @@ except ImportError:
open_api_models = None
tea_util_models = None
from gateway.config import Platform, PlatformConfig
from gateway.platforms.helpers import MessageDeduplicator
from gateway.platforms.base import (
from hermes_agent.gateway.config import Platform, PlatformConfig
from hermes_agent.gateway.platforms.helpers import MessageDeduplicator
from hermes_agent.gateway.platforms.base import (
BasePlatformAdapter,
MessageEvent,
MessageType,

View file

@ -36,15 +36,11 @@ except ImportError:
Intents = Any
commands = None
import sys
from pathlib import Path as _Path
sys.path.insert(0, str(_Path(__file__).resolve().parents[2]))
from gateway.config import Platform, PlatformConfig
from hermes_agent.gateway.config import Platform, PlatformConfig
import re
from gateway.platforms.helpers import MessageDeduplicator, ThreadParticipationTracker
from gateway.platforms.base import (
from hermes_agent.gateway.platforms.helpers import MessageDeduplicator, ThreadParticipationTracker
from hermes_agent.gateway.platforms.base import (
BasePlatformAdapter,
MessageEvent,
MessageType,
@ -57,7 +53,7 @@ from gateway.platforms.base import (
cache_document_from_bytes,
SUPPORTED_DOCUMENT_TYPES,
)
from tools.url_safety import is_safe_url
from hermes_agent.tools.security.urls import is_safe_url
def _clean_discord_id(entry: str) -> str:
@ -601,7 +597,7 @@ class DiscordAdapter(BasePlatformAdapter):
intents.voice_states = True
# Resolve proxy (DISCORD_PROXY > generic env vars > macOS system proxy)
from gateway.platforms.base import resolve_proxy_url, proxy_kwargs_for_bot
from hermes_agent.gateway.platforms.base import resolve_proxy_url, proxy_kwargs_for_bot
proxy_url = resolve_proxy_url(platform_env_var="DISCORD_PROXY")
if proxy_url:
logger.info("[%s] Using proxy for Discord: %s", self.name, proxy_url)
@ -970,7 +966,7 @@ class DiscordAdapter(BasePlatformAdapter):
reported in ``raw_response['warnings']`` so the caller can surface
partial-send issues.
"""
from tools.send_message_tool import _derive_forum_thread_name
from hermes_agent.tools.send_message import _derive_forum_thread_name
formatted = self.format_message(content)
chunks = self.truncate_message(formatted, self.MAX_MESSAGE_LENGTH)
@ -1032,7 +1028,7 @@ class DiscordAdapter(BasePlatformAdapter):
ForumChannel accepts the same file/files/content kwargs as
``channel.send``, creating the thread and starter message atomically.
"""
from tools.send_message_tool import _derive_forum_thread_name
from hermes_agent.tools.send_message import _derive_forum_thread_name
if not thread_name:
# Prefer the text content, fall back to the first attached
@ -1507,7 +1503,7 @@ class DiscordAdapter(BasePlatformAdapter):
async def _process_voice_input(self, guild_id: int, user_id: int, pcm_data: bytes):
"""Convert PCM -> WAV -> STT -> callback."""
from tools.voice_mode import is_whisper_hallucination
from hermes_agent.tools.media.voice import is_whisper_hallucination
tmp_f = tempfile.NamedTemporaryFile(suffix=".wav", prefix="vc_listen_", delete=False)
wav_path = tmp_f.name
@ -1515,7 +1511,7 @@ class DiscordAdapter(BasePlatformAdapter):
try:
await asyncio.to_thread(VoiceReceiver.pcm_to_wav, pcm_data, wav_path)
from tools.transcription_tools import transcribe_audio
from hermes_agent.tools.media.transcription import transcribe_audio
result = await asyncio.to_thread(transcribe_audio, wav_path)
if not result.get("success"):
@ -1627,7 +1623,7 @@ class DiscordAdapter(BasePlatformAdapter):
# Download the image and send as a Discord file attachment
# (Discord renders attachments inline, unlike plain URLs)
from gateway.platforms.base import resolve_proxy_url, proxy_kwargs_for_aiohttp
from hermes_agent.gateway.platforms.base import resolve_proxy_url, proxy_kwargs_for_aiohttp
_proxy = resolve_proxy_url(platform_env_var="DISCORD_PROXY")
_sess_kw, _req_kw = proxy_kwargs_for_aiohttp(_proxy)
async with aiohttp.ClientSession(**_sess_kw) as session:
@ -1706,7 +1702,7 @@ class DiscordAdapter(BasePlatformAdapter):
# Download the GIF and send as a Discord file attachment
# (Discord renders .gif attachments as auto-playing animations inline)
from gateway.platforms.base import resolve_proxy_url, proxy_kwargs_for_aiohttp
from hermes_agent.gateway.platforms.base import resolve_proxy_url, proxy_kwargs_for_aiohttp
_proxy = resolve_proxy_url(platform_env_var="DISCORD_PROXY")
_sess_kw, _req_kw = proxy_kwargs_for_aiohttp(_proxy)
async with aiohttp.ClientSession(**_sess_kw) as session:
@ -2137,7 +2133,7 @@ class DiscordAdapter(BasePlatformAdapter):
# hermes_cli/commands.py automatically appear as Discord slash
# commands without needing a manual entry here.
try:
from hermes_cli.commands import COMMAND_REGISTRY, _is_gateway_available, _resolve_config_gates
from hermes_agent.cli.commands import COMMAND_REGISTRY, _is_gateway_available, _resolve_config_gates
already_registered = set()
try:
@ -2227,7 +2223,7 @@ class DiscordAdapter(BasePlatformAdapter):
skill name and its description.
"""
try:
from hermes_cli.commands import discord_skill_commands_by_category
from hermes_agent.cli.commands import discord_skill_commands_by_category
existing_names = set()
try:
@ -2481,7 +2477,7 @@ class DiscordAdapter(BasePlatformAdapter):
def _resolve_channel_prompt(self, channel_id: str, parent_id: str | None = None) -> str | None:
"""Resolve a Discord per-channel prompt, preferring the exact channel over its parent."""
from gateway.platforms.base import resolve_channel_prompt
from hermes_agent.gateway.platforms.base import resolve_channel_prompt
return resolve_channel_prompt(self.config.extra, channel_id, parent_id)
def _discord_require_mention(self) -> bool:
@ -2747,7 +2743,7 @@ class DiscordAdapter(BasePlatformAdapter):
channel = await self._client.fetch_channel(int(target_id))
try:
from hermes_cli.providers import get_label
from hermes_agent.cli.providers import get_label
provider_label = get_label(current_provider)
except Exception:
provider_label = current_provider
@ -2932,7 +2928,7 @@ class DiscordAdapter(BasePlatformAdapter):
f"Blocked unsafe attachment URL (SSRF protection): {att.url}"
)
import aiohttp
from gateway.platforms.base import resolve_proxy_url, proxy_kwargs_for_aiohttp
from hermes_agent.gateway.platforms.base import resolve_proxy_url, proxy_kwargs_for_aiohttp
_proxy = resolve_proxy_url(platform_env_var="DISCORD_PROXY")
_sess_kw, _req_kw = proxy_kwargs_for_aiohttp(_proxy)
async with aiohttp.ClientSession(**_sess_kw) as session:
@ -3235,7 +3231,7 @@ class DiscordAdapter(BasePlatformAdapter):
def _text_batch_key(self, event: MessageEvent) -> str:
"""Session-scoped key for text message batching."""
from gateway.session import build_session_key
from hermes_agent.gateway.session import build_session_key
return build_session_key(
event.source,
group_sessions_per_user=self.config.extra.get("group_sessions_per_user", True),
@ -3372,7 +3368,7 @@ if DISCORD_AVAILABLE:
# Unblock the waiting agent thread via the gateway approval queue
try:
from tools.approval import resolve_gateway_approval
from hermes_agent.tools.security.approval import resolve_gateway_approval
count = resolve_gateway_approval(self.session_key, choice)
logger.info(
"Discord button resolved %d approval(s) for session %s (choice=%s, user=%s)",
@ -3460,7 +3456,7 @@ if DISCORD_AVAILABLE:
# Write response file
try:
from hermes_constants import get_hermes_home
from hermes_agent.constants import get_hermes_home
home = get_hermes_home()
response_path = home / ".update_response"
tmp = response_path.with_suffix(".tmp")
@ -3675,7 +3671,7 @@ if DISCORD_AVAILABLE:
self._build_provider_select()
try:
from hermes_cli.providers import get_label
from hermes_agent.cli.providers import get_label
provider_label = get_label(self.current_provider)
except Exception:
provider_label = self.current_provider

View file

@ -32,7 +32,7 @@ from email import encoders
from pathlib import Path
from typing import Any, Dict, List, Optional
from gateway.platforms.base import (
from hermes_agent.gateway.platforms.base import (
BasePlatformAdapter,
MessageEvent,
MessageType,
@ -40,7 +40,7 @@ from gateway.platforms.base import (
cache_document_from_bytes,
cache_image_from_bytes,
)
from gateway.config import Platform, PlatformConfig
from hermes_agent.gateway.config import Platform, PlatformConfig
logger = logging.getLogger(__name__)
# Automated sender patterns — emails from these are silently ignored

View file

@ -95,8 +95,8 @@ except ImportError:
FEISHU_WEBSOCKET_AVAILABLE = websockets is not None
FEISHU_WEBHOOK_AVAILABLE = aiohttp is not None
from gateway.config import Platform, PlatformConfig
from gateway.platforms.base import (
from hermes_agent.gateway.config import Platform, PlatformConfig
from hermes_agent.gateway.platforms.base import (
BasePlatformAdapter,
MessageEvent,
MessageType,
@ -108,8 +108,8 @@ from gateway.platforms.base import (
cache_audio_from_bytes,
cache_image_from_bytes,
)
from gateway.status import acquire_scoped_lock, release_scoped_lock
from hermes_constants import get_hermes_home
from hermes_agent.gateway.status import acquire_scoped_lock, release_scoped_lock
from hermes_agent.constants import get_hermes_home
logger = logging.getLogger(__name__)
@ -414,7 +414,7 @@ def _strip_markdown_to_plain_text(text: str) -> str:
Feishu-specific patterns (blockquotes, strikethrough, underline tags,
horizontal rules, \\r\\n normalisation).
"""
from gateway.platforms.helpers import strip_markdown
from hermes_agent.gateway.platforms.helpers import strip_markdown
plain = text.replace("\r\n", "\n")
plain = _MARKDOWN_LINK_RE.sub(lambda m: f"{m.group(1)} ({m.group(2).strip()})", plain)
plain = re.sub(r"^>\s?", "", plain, flags=re.MULTILINE)
@ -2039,7 +2039,7 @@ class FeishuAdapter(BasePlatformAdapter):
logging, and reaction. Scheduling follows the same
``run_coroutine_threadsafe`` pattern used by ``_on_message_event``.
"""
from gateway.platforms.feishu_comment import handle_drive_comment_event
from hermes_agent.gateway.platforms.feishu_comment import handle_drive_comment_event
loop = self._loop
if not self._loop_accepts_callbacks(loop):
@ -2151,7 +2151,7 @@ class FeishuAdapter(BasePlatformAdapter):
logger.debug("[Feishu] Approval %s already resolved or unknown", approval_id)
return
try:
from tools.approval import resolve_gateway_approval
from hermes_agent.tools.security.approval import resolve_gateway_approval
count = resolve_gateway_approval(state["session_key"], choice)
logger.info(
"Feishu button resolved %d approval(s) for session %s (choice=%s, user=%s)",
@ -2542,7 +2542,7 @@ class FeishuAdapter(BasePlatformAdapter):
)
def _media_batch_key(self, event: MessageEvent) -> str:
from gateway.session import build_session_key
from hermes_agent.gateway.session import build_session_key
session_key = build_session_key(
event.source,
@ -2619,7 +2619,7 @@ class FeishuAdapter(BasePlatformAdapter):
default_ext: str,
preferred_name: str,
) -> tuple[str, str]:
from tools.url_safety import is_safe_url
from hermes_agent.tools.security.urls import is_safe_url
if not is_safe_url(file_url):
raise ValueError(f"Blocked unsafe URL (SSRF protection): {file_url[:80]}")
@ -2822,7 +2822,7 @@ class FeishuAdapter(BasePlatformAdapter):
def _text_batch_key(self, event: MessageEvent) -> str:
"""Return the session-scoped key used for Feishu text aggregation."""
from gateway.session import build_session_key
from hermes_agent.gateway.session import build_session_key
return build_session_key(
event.source,

Some files were not shown because too many files have changed in this diff Show more