mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-30 16:01:49 +08:00
Compare commits
22 Commits
feat/langf
...
sid/founda
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
987962f453 | ||
|
|
25072fe690 | ||
|
|
ff99611e16 | ||
|
|
76aebd73c3 | ||
|
|
a1e667b9f2 | ||
|
|
4b16341975 | ||
|
|
65ca3ba93b | ||
|
|
8bff8bf2c0 | ||
|
|
acd1f17b88 | ||
|
|
850973295e | ||
|
|
847ffca715 | ||
|
|
67bc441099 | ||
|
|
a9ed7cb3b4 | ||
|
|
15ac253b11 | ||
|
|
fb6d37495b | ||
|
|
72e7c0ce34 | ||
|
|
f8d2365795 | ||
|
|
ca2b6a529e | ||
|
|
224e6d46d9 | ||
|
|
d3dde0b459 | ||
|
|
3f4c5ac71e | ||
|
|
08c378356d |
5
.git-blame-ignore-revs
Normal file
5
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# hermes_agent package restructure (PR 1/3)
|
||||||
|
# Commit 2: pure git mv — all source files into hermes_agent/
|
||||||
|
65ca3ba93b3fa7fd2b15af5b62d54020061f3672
|
||||||
|
# Commit 3: rewrite all imports for hermes_agent package
|
||||||
|
4b16341975a1217588054f567d0f76dc5a3cc481
|
||||||
137
AGENTS.md
137
AGENTS.md
@@ -12,68 +12,59 @@ source venv/bin/activate # ALWAYS activate before running Python
|
|||||||
|
|
||||||
```
|
```
|
||||||
hermes-agent/
|
hermes-agent/
|
||||||
├── run_agent.py # AIAgent class — core conversation loop
|
├── hermes_agent/ # Single installable package
|
||||||
├── model_tools.py # Tool orchestration, discover_builtin_tools(), handle_function_call()
|
│ ├── agent/ # Core conversation loop and agent internals
|
||||||
├── toolsets.py # Toolset definitions, _HERMES_CORE_TOOLS list
|
│ │ ├── loop.py # AIAgent class — core conversation loop
|
||||||
├── cli.py # HermesCLI class — interactive CLI orchestrator
|
│ │ ├── prompt_builder.py # System prompt assembly
|
||||||
├── hermes_state.py # SessionDB — SQLite session store (FTS5 search)
|
│ │ ├── context/ # Context management (engine, compressor, references)
|
||||||
├── agent/ # Agent internals
|
│ │ ├── memory/ # Memory management (manager, provider)
|
||||||
│ ├── prompt_builder.py # System prompt assembly
|
│ │ ├── image_gen/ # Image generation (provider, registry)
|
||||||
│ ├── context_compressor.py # Auto context compression
|
│ │ ├── display.py # KawaiiSpinner, tool preview formatting
|
||||||
│ ├── prompt_caching.py # Anthropic prompt caching
|
│ │ ├── skill_commands.py # Skill slash commands (shared CLI/gateway)
|
||||||
│ ├── auxiliary_client.py # Auxiliary LLM client (vision, summarization)
|
│ │ └── trajectory.py # Trajectory saving helpers
|
||||||
│ ├── model_metadata.py # Model context lengths, token estimation
|
│ ├── providers/ # LLM provider adapters and transports
|
||||||
│ ├── models_dev.py # models.dev registry integration (provider-aware context)
|
│ │ ├── anthropic_adapter.py # Anthropic adapter
|
||||||
│ ├── display.py # KawaiiSpinner, tool preview formatting
|
│ │ ├── anthropic_transport.py # Anthropic transport
|
||||||
│ ├── skill_commands.py # Skill slash commands (shared CLI/gateway)
|
│ │ ├── metadata.py # Model context lengths, token estimation
|
||||||
│ └── trajectory.py # Trajectory saving helpers
|
│ │ ├── auxiliary.py # Auxiliary LLM client (vision, summarization)
|
||||||
├── hermes_cli/ # CLI subcommands and setup
|
│ │ ├── caching.py # Anthropic prompt caching
|
||||||
│ ├── main.py # Entry point — all `hermes` subcommands
|
│ │ └── credential_pool.py # Credential management
|
||||||
│ ├── config.py # DEFAULT_CONFIG, OPTIONAL_ENV_VARS, migration
|
│ ├── tools/ # Tool implementations
|
||||||
│ ├── commands.py # Slash command definitions + SlashCommandCompleter
|
│ │ ├── dispatch.py # Tool orchestration, discover_builtin_tools()
|
||||||
│ ├── callbacks.py # Terminal callbacks (clarify, sudo, approval)
|
│ │ ├── toolsets.py # Toolset definitions
|
||||||
│ ├── setup.py # Interactive setup wizard
|
│ │ ├── registry.py # Central tool registry
|
||||||
│ ├── skin_engine.py # Skin/theme engine — CLI visual customization
|
│ │ ├── terminal.py # Terminal orchestration
|
||||||
│ ├── skills_config.py # `hermes skills` — enable/disable skills per platform
|
│ │ ├── browser/ # Browser tools (tool, cdp, camofox, providers/)
|
||||||
│ ├── tools_config.py # `hermes tools` — enable/disable tools per platform
|
│ │ ├── mcp/ # MCP client and server
|
||||||
│ ├── skills_hub.py # `/skills` slash command (search, browse, install)
|
│ │ ├── skills/ # Skill management (manager, tool, hub, guard, sync)
|
||||||
│ ├── models.py # Model catalog, provider model lists
|
│ │ ├── media/ # Voice, TTS, transcription, image gen
|
||||||
│ ├── model_switch.py # Shared /model switch pipeline (CLI + gateway)
|
│ │ ├── files/ # File operations (tools, operations, state)
|
||||||
│ └── auth.py # Provider credential resolution
|
│ │ └── security/ # Path security, URL safety, approval
|
||||||
├── tools/ # Tool implementations (one file per tool)
|
│ ├── backends/ # Terminal backends (local, docker, ssh, modal, daytona, singularity)
|
||||||
│ ├── registry.py # Central tool registry (schemas, handlers, dispatch)
|
│ ├── cli/ # CLI subcommands and setup
|
||||||
│ ├── approval.py # Dangerous command detection
|
│ │ ├── main.py # Entry point — all `hermes` subcommands
|
||||||
│ ├── terminal_tool.py # Terminal orchestration
|
│ │ ├── repl.py # HermesCLI class — interactive CLI orchestrator
|
||||||
│ ├── process_registry.py # Background process management
|
│ │ ├── config.py # DEFAULT_CONFIG, OPTIONAL_ENV_VARS, migration
|
||||||
│ ├── file_tools.py # File read/write/search/patch
|
│ │ ├── commands.py # Slash command definitions
|
||||||
│ ├── web_tools.py # Web search/extract (Parallel + Firecrawl)
|
│ │ ├── auth/ # Provider credential resolution
|
||||||
│ ├── browser_tool.py # Browserbase browser automation
|
│ │ ├── models/ # Model catalog, provider lists, switching
|
||||||
│ ├── code_execution_tool.py # execute_code sandbox
|
│ │ └── ui/ # Banner, colors, skin engine, callbacks, tips
|
||||||
│ ├── delegate_tool.py # Subagent delegation
|
│ ├── gateway/ # Messaging platform gateway
|
||||||
│ ├── mcp_tool.py # MCP client (~1050 lines)
|
│ │ ├── run.py # Main loop, slash commands, message dispatch
|
||||||
│ └── environments/ # Terminal backends (local, docker, ssh, modal, daytona, singularity)
|
│ │ ├── session.py # SessionStore — conversation persistence
|
||||||
├── gateway/ # Messaging platform gateway
|
│ │ └── platforms/ # Adapters: telegram, discord, slack, whatsapp, etc.
|
||||||
│ ├── run.py # Main loop, slash commands, message dispatch
|
│ ├── acp/ # ACP server (VS Code / Zed / JetBrains integration)
|
||||||
│ ├── session.py # SessionStore — conversation persistence
|
│ ├── cron/ # Scheduler (jobs.py, scheduler.py)
|
||||||
│ └── platforms/ # Adapters: telegram, discord, slack, whatsapp, homeassistant, signal, qqbot
|
│ ├── plugins/ # Plugin system (memory providers, context engines)
|
||||||
├── ui-tui/ # Ink (React) terminal UI — `hermes --tui`
|
│ ├── constants.py # Shared constants
|
||||||
│ ├── src/entry.tsx # TTY gate + render()
|
│ ├── state.py # SessionDB — SQLite session store
|
||||||
│ ├── src/app.tsx # Main state machine and UI
|
│ ├── logging.py # Logging configuration
|
||||||
│ ├── src/gatewayClient.ts # Child process + JSON-RPC bridge
|
│ └── utils.py # Shared utilities
|
||||||
│ ├── src/app/ # Decomposed app logic (event handler, slash handler, stores, hooks)
|
|
||||||
│ ├── src/components/ # Ink components (branding, markdown, prompts, pickers, etc.)
|
|
||||||
│ ├── src/hooks/ # useCompletion, useInputHistory, useQueue, useVirtualHistory
|
|
||||||
│ └── src/lib/ # Pure helpers (history, osc52, text, rpc, messages)
|
|
||||||
├── tui_gateway/ # Python JSON-RPC backend for the TUI
|
├── tui_gateway/ # Python JSON-RPC backend for the TUI
|
||||||
│ ├── entry.py # stdio entrypoint
|
├── ui-tui/ # Ink (React) terminal UI — `hermes --tui`
|
||||||
│ ├── server.py # RPC handlers and session logic
|
|
||||||
│ ├── render.py # Optional rich/ANSI bridge
|
|
||||||
│ └── slash_worker.py # Persistent HermesCLI subprocess for slash commands
|
|
||||||
├── acp_adapter/ # ACP server (VS Code / Zed / JetBrains integration)
|
|
||||||
├── cron/ # Scheduler (jobs.py, scheduler.py)
|
|
||||||
├── environments/ # RL training environments (Atropos)
|
├── environments/ # RL training environments (Atropos)
|
||||||
├── tests/ # Pytest suite (~3000 tests)
|
├── tests/ # Pytest suite
|
||||||
└── batch_runner.py # Parallel batch processing
|
└── web/ # Vite + React web dashboard
|
||||||
```
|
```
|
||||||
|
|
||||||
**User config:** `~/.hermes/config.yaml` (settings), `~/.hermes/.env` (API keys)
|
**User config:** `~/.hermes/config.yaml` (settings), `~/.hermes/.env` (API keys)
|
||||||
@@ -81,18 +72,18 @@ hermes-agent/
|
|||||||
## File Dependency Chain
|
## File Dependency Chain
|
||||||
|
|
||||||
```
|
```
|
||||||
tools/registry.py (no deps — imported by all tool files)
|
hermes_agent/tools/registry.py (no deps — imported by all tool files)
|
||||||
↑
|
↑
|
||||||
tools/*.py (each calls registry.register() at import time)
|
hermes_agent/tools/*.py (each calls registry.register() at import time)
|
||||||
↑
|
↑
|
||||||
model_tools.py (imports tools/registry + triggers tool discovery)
|
hermes_agent/tools/dispatch.py (imports registry + triggers tool discovery)
|
||||||
↑
|
↑
|
||||||
run_agent.py, cli.py, batch_runner.py, environments/
|
hermes_agent/agent/loop.py, hermes_agent/cli/repl.py, environments/
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## AIAgent Class (run_agent.py)
|
## AIAgent Class (hermes_agent/agent/loop.py)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class AIAgent:
|
class AIAgent:
|
||||||
@@ -138,14 +129,14 @@ Messages follow OpenAI format: `{"role": "system/user/assistant/tool", ...}`. Re
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## CLI Architecture (cli.py)
|
## CLI Architecture (hermes_agent/cli/repl.py)
|
||||||
|
|
||||||
- **Rich** for banner/panels, **prompt_toolkit** for input with autocomplete
|
- **Rich** for banner/panels, **prompt_toolkit** for input with autocomplete
|
||||||
- **KawaiiSpinner** (`agent/display.py`) — animated faces during API calls, `┊` activity feed for tool results
|
- **KawaiiSpinner** (`hermes_agent/agent/display.py`) — animated faces during API calls, `┊` activity feed for tool results
|
||||||
- `load_cli_config()` in cli.py merges hardcoded defaults + user config YAML
|
- `load_cli_config()` in repl.py merges hardcoded defaults + user config YAML
|
||||||
- **Skin engine** (`hermes_cli/skin_engine.py`) — data-driven CLI theming; initialized from `display.skin` config key at startup; skins customize banner colors, spinner faces/verbs/wings, tool prefix, response box, branding text
|
- **Skin engine** (`hermes_agent/cli/ui/skin_engine.py`) — data-driven CLI theming; initialized from `display.skin` config key at startup; skins customize banner colors, spinner faces/verbs/wings, tool prefix, response box, branding text
|
||||||
- `process_command()` is a method on `HermesCLI` — dispatches on canonical command name resolved via `resolve_command()` from the central registry
|
- `process_command()` is a method on `HermesCLI` — dispatches on canonical command name resolved via `resolve_command()` from the central registry
|
||||||
- Skill slash commands: `agent/skill_commands.py` scans `~/.hermes/skills/`, injects as **user message** (not system prompt) to preserve prompt caching
|
- Skill slash commands: `hermes_agent/agent/skill_commands.py` scans `~/.hermes/skills/`, injects as **user message** (not system prompt) to preserve prompt caching
|
||||||
|
|
||||||
### Slash Command Registry (`hermes_cli/commands.py`)
|
### Slash Command Registry (`hermes_cli/commands.py`)
|
||||||
|
|
||||||
@@ -272,7 +263,7 @@ registry.register(
|
|||||||
|
|
||||||
**2. Add to `toolsets.py`** — either `_HERMES_CORE_TOOLS` (all platforms) or a new toolset.
|
**2. Add to `toolsets.py`** — either `_HERMES_CORE_TOOLS` (all platforms) or a new toolset.
|
||||||
|
|
||||||
Auto-discovery: any `tools/*.py` file with a top-level `registry.register()` call is imported automatically — no manual import list to maintain.
|
Auto-discovery: any `hermes_agent/tools/*.py` file with a top-level `registry.register()` call is imported automatically — no manual import list to maintain.
|
||||||
|
|
||||||
The registry handles schema collection, dispatch, availability checking, and error wrapping. All handlers MUST return a JSON string.
|
The registry handles schema collection, dispatch, availability checking, and error wrapping. All handlers MUST return a JSON string.
|
||||||
|
|
||||||
@@ -498,11 +489,11 @@ Rendering bugs in tmux/iTerm2 — ghosting on scroll. Use `curses` (stdlib) inst
|
|||||||
### DO NOT use `\033[K` (ANSI erase-to-EOL) in spinner/display code
|
### DO NOT use `\033[K` (ANSI erase-to-EOL) in spinner/display code
|
||||||
Leaks as literal `?[K` text under `prompt_toolkit`'s `patch_stdout`. Use space-padding: `f"\r{line}{' ' * pad}"`.
|
Leaks as literal `?[K` text under `prompt_toolkit`'s `patch_stdout`. Use space-padding: `f"\r{line}{' ' * pad}"`.
|
||||||
|
|
||||||
### `_last_resolved_tool_names` is a process-global in `model_tools.py`
|
### `_last_resolved_tool_names` is a process-global in `hermes_agent/tools/dispatch.py`
|
||||||
`_run_single_child()` in `delegate_tool.py` saves and restores this global around subagent execution. If you add new code that reads this global, be aware it may be temporarily stale during child agent runs.
|
`_run_single_child()` in `delegate_tool.py` saves and restores this global around subagent execution. If you add new code that reads this global, be aware it may be temporarily stale during child agent runs.
|
||||||
|
|
||||||
### DO NOT hardcode cross-tool references in schema descriptions
|
### DO NOT hardcode cross-tool references in schema descriptions
|
||||||
Tool schema descriptions must not mention tools from other toolsets by name (e.g., `browser_navigate` saying "prefer web_search"). Those tools may be unavailable (missing API keys, disabled toolset), causing the model to hallucinate calls to non-existent tools. If a cross-reference is needed, add it dynamically in `get_tool_definitions()` in `model_tools.py` — see the `browser_navigate` / `execute_code` post-processing blocks for the pattern.
|
Tool schema descriptions must not mention tools from other toolsets by name (e.g., `browser_navigate` saying "prefer web_search"). Those tools may be unavailable (missing API keys, disabled toolset), causing the model to hallucinate calls to non-existent tools. If a cross-reference is needed, add it dynamically in `get_tool_definitions()` in `hermes_agent/tools/dispatch.py` — see the `browser_navigate` / `execute_code` post-processing blocks for the pattern.
|
||||||
|
|
||||||
### Tests must not write to `~/.hermes/`
|
### Tests must not write to `~/.hermes/`
|
||||||
The `_isolate_hermes_home` autouse fixture in `tests/conftest.py` redirects `HERMES_HOME` to a temp dir. Never hardcode `~/.hermes/` paths in tests.
|
The `_isolate_hermes_home` autouse fixture in `tests/conftest.py` redirects `HERMES_HOME` to a temp dir. Never hardcode `~/.hermes/` paths in tests.
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ RUN npm install --prefer-offline --no-audit && \
|
|||||||
# .dockerignore excludes node_modules, so the installs above survive.
|
# .dockerignore excludes node_modules, so the installs above survive.
|
||||||
COPY --chown=hermes:hermes . .
|
COPY --chown=hermes:hermes . .
|
||||||
|
|
||||||
# Build web dashboard (Vite outputs to hermes_cli/web_dist/)
|
# Build web dashboard (Vite outputs to hermes_agent/cli/web_dist/)
|
||||||
RUN cd web && npm run build
|
RUN cd web && npm run build
|
||||||
|
|
||||||
# ---------- Python virtualenv ----------
|
# ---------- Python virtualenv ----------
|
||||||
@@ -48,7 +48,7 @@ RUN uv venv && \
|
|||||||
uv pip install --no-cache-dir -e ".[all]"
|
uv pip install --no-cache-dir -e ".[all]"
|
||||||
|
|
||||||
# ---------- Runtime ----------
|
# ---------- Runtime ----------
|
||||||
ENV HERMES_WEB_DIST=/opt/hermes/hermes_cli/web_dist
|
ENV HERMES_WEB_DIST=/opt/hermes/hermes_agent/cli/web_dist
|
||||||
ENV HERMES_HOME=/opt/data
|
ENV HERMES_HOME=/opt/data
|
||||||
VOLUME [ "/opt/data" ]
|
VOLUME [ "/opt/data" ]
|
||||||
ENTRYPOINT [ "/opt/hermes/docker/entrypoint.sh" ]
|
ENTRYPOINT [ "/opt/hermes/docker/entrypoint.sh" ]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
graft hermes_agent
|
||||||
graft skills
|
graft skills
|
||||||
graft optional-skills
|
graft optional-skills
|
||||||
global-exclude __pycache__
|
global-exclude __pycache__
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ echo "📝 Logging to: $LOG_FILE"
|
|||||||
# Point to the example dataset in this directory
|
# Point to the example dataset in this directory
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
python batch_runner.py \
|
python scripts/batch_runner.py \
|
||||||
--dataset_file="$SCRIPT_DIR/example_browser_tasks.jsonl" \
|
--dataset_file="$SCRIPT_DIR/example_browser_tasks.jsonl" \
|
||||||
--batch_size=5 \
|
--batch_size=5 \
|
||||||
--run_name="browser_tasks_example" \
|
--run_name="browser_tasks_example" \
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
# Generates tool-calling trajectories for multi-step web research tasks.
|
# Generates tool-calling trajectories for multi-step web research tasks.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# python batch_runner.py \
|
# python scripts/batch_runner.py \
|
||||||
# --config datagen-config-examples/web_research.yaml \
|
# --config datagen-config-examples/web_research.yaml \
|
||||||
# --run_name web_research_v1
|
# --run_name web_research_v1
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ fi
|
|||||||
|
|
||||||
# Sync bundled skills (manifest-based so user edits are preserved)
|
# Sync bundled skills (manifest-based so user edits are preserved)
|
||||||
if [ -d "$INSTALL_DIR/skills" ]; then
|
if [ -d "$INSTALL_DIR/skills" ]; then
|
||||||
python3 "$INSTALL_DIR/tools/skills_sync.py"
|
hermes-skills-sync
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec hermes "$@"
|
exec hermes "$@"
|
||||||
|
|||||||
@@ -18,11 +18,14 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Any, Dict, List, Optional, Set
|
from typing import Any, Dict, List, Optional, Set, TYPE_CHECKING
|
||||||
|
|
||||||
from model_tools import handle_function_call
|
if TYPE_CHECKING:
|
||||||
from tools.terminal_tool import get_active_env
|
from hermes_agent.tools.budget_config import BudgetConfig
|
||||||
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()
|
# 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
|
# (e.g., the Modal/Docker/Daytona terminal backends). Running them in a separate
|
||||||
@@ -161,7 +164,7 @@ class HermesAgentLoop:
|
|||||||
thresholds, per-turn aggregate budget, and preview size.
|
thresholds, per-turn aggregate budget, and preview size.
|
||||||
If None, uses DEFAULT_BUDGET (current hardcoded values).
|
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.server = server
|
||||||
self.tool_schemas = tool_schemas
|
self.tool_schemas = tool_schemas
|
||||||
self.valid_tool_names = valid_tool_names
|
self.valid_tool_names = valid_tool_names
|
||||||
@@ -187,7 +190,7 @@ class HermesAgentLoop:
|
|||||||
tool_errors: List[ToolError] = []
|
tool_errors: List[ToolError] = []
|
||||||
|
|
||||||
# Per-loop TodoStore for the todo tool (ephemeral, dies with the loop)
|
# 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()
|
_todo_store = TodoStore()
|
||||||
|
|
||||||
# Extract user task from first user message for browser_snapshot context
|
# Extract user task from first user message for browser_snapshot context
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ from atroposlib.envs.server_handling.server_manager import APIServerConfig
|
|||||||
from environments.agent_loop import AgentResult, HermesAgentLoop
|
from environments.agent_loop import AgentResult, HermesAgentLoop
|
||||||
from environments.hermes_base_env import HermesAgentBaseEnv, HermesAgentEnvConfig
|
from environments.hermes_base_env import HermesAgentBaseEnv, HermesAgentEnvConfig
|
||||||
from environments.tool_context import ToolContext
|
from environments.tool_context import ToolContext
|
||||||
from tools.terminal_tool import (
|
from hermes_agent.tools.terminal import (
|
||||||
register_task_env_overrides,
|
register_task_env_overrides,
|
||||||
clear_task_env_overrides,
|
clear_task_env_overrides,
|
||||||
cleanup_vm,
|
cleanup_vm,
|
||||||
@@ -876,7 +876,7 @@ class TerminalBench2EvalEnv(HermesAgentBaseEnv):
|
|||||||
# Let cancellations propagate (finally blocks run cleanup_vm)
|
# Let cancellations propagate (finally blocks run cleanup_vm)
|
||||||
await asyncio.gather(*eval_tasks, return_exceptions=True)
|
await asyncio.gather(*eval_tasks, return_exceptions=True)
|
||||||
# Belt-and-suspenders: clean up any remaining sandboxes
|
# 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()
|
cleanup_all_environments()
|
||||||
print("All sandboxes cleaned up.")
|
print("All sandboxes cleaned up.")
|
||||||
return
|
return
|
||||||
@@ -984,7 +984,7 @@ class TerminalBench2EvalEnv(HermesAgentBaseEnv):
|
|||||||
|
|
||||||
# Kill all remaining sandboxes. Timed-out tasks leave orphaned thread
|
# Kill all remaining sandboxes. Timed-out tasks leave orphaned thread
|
||||||
# pool workers still executing commands -- cleanup_all stops them.
|
# 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...")
|
print("\nCleaning up all sandboxes...")
|
||||||
cleanup_all_environments()
|
cleanup_all_environments()
|
||||||
|
|
||||||
|
|||||||
@@ -709,7 +709,7 @@ class YCBenchEvalEnv(HermesAgentBaseEnv):
|
|||||||
tqdm.write("\n[INTERRUPTED] Stopping evaluation...")
|
tqdm.write("\n[INTERRUPTED] Stopping evaluation...")
|
||||||
pbar.close()
|
pbar.close()
|
||||||
try:
|
try:
|
||||||
from tools.terminal_tool import cleanup_all_environments
|
from hermes_agent.tools.terminal import cleanup_all_environments
|
||||||
cleanup_all_environments()
|
cleanup_all_environments()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -819,7 +819,7 @@ class YCBenchEvalEnv(HermesAgentBaseEnv):
|
|||||||
print(f"Results saved to: {self._streaming_path}")
|
print(f"Results saved to: {self._streaming_path}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from tools.terminal_tool import cleanup_all_environments
|
from hermes_agent.tools.terminal import cleanup_all_environments
|
||||||
cleanup_all_environments()
|
cleanup_all_environments()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -62,15 +62,15 @@ from atroposlib.type_definitions import Item
|
|||||||
|
|
||||||
from environments.agent_loop import AgentResult, HermesAgentLoop
|
from environments.agent_loop import AgentResult, HermesAgentLoop
|
||||||
from environments.tool_context import ToolContext
|
from environments.tool_context import ToolContext
|
||||||
from tools.budget_config import (
|
from hermes_agent.tools.budget_config import (
|
||||||
DEFAULT_RESULT_SIZE_CHARS,
|
DEFAULT_RESULT_SIZE_CHARS,
|
||||||
DEFAULT_TURN_BUDGET_CHARS,
|
DEFAULT_TURN_BUDGET_CHARS,
|
||||||
DEFAULT_PREVIEW_SIZE_CHARS,
|
DEFAULT_PREVIEW_SIZE_CHARS,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Import hermes-agent toolset infrastructure
|
# Import hermes-agent toolset infrastructure
|
||||||
from model_tools import get_tool_definitions
|
from hermes_agent.tools.dispatch import get_tool_definitions
|
||||||
from toolset_distributions import sample_toolsets_from_distribution
|
from hermes_agent.tools.distributions import sample_toolsets_from_distribution
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -209,7 +209,7 @@ class HermesAgentEnvConfig(BaseEnvConfig):
|
|||||||
|
|
||||||
def build_budget_config(self):
|
def build_budget_config(self):
|
||||||
"""Build a BudgetConfig from env config fields."""
|
"""Build a BudgetConfig from env config fields."""
|
||||||
from tools.budget_config import BudgetConfig
|
from hermes_agent.tools.budget_config import BudgetConfig
|
||||||
return BudgetConfig(
|
return BudgetConfig(
|
||||||
default_result_size=self.default_result_size_chars,
|
default_result_size=self.default_result_size_chars,
|
||||||
turn_budget=self.turn_budget_chars,
|
turn_budget=self.turn_budget_chars,
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ from typing import Any, Dict, List, Optional
|
|||||||
import asyncio
|
import asyncio
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
|
|
||||||
from model_tools import handle_function_call
|
from hermes_agent.tools.dispatch import handle_function_call
|
||||||
from tools.terminal_tool import cleanup_vm
|
from hermes_agent.tools.terminal import cleanup_vm
|
||||||
from tools.browser_tool import cleanup_browser
|
from hermes_agent.tools.browser.tool import cleanup_browser
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -446,7 +446,7 @@ class ToolContext:
|
|||||||
"""
|
"""
|
||||||
# Kill any background processes from this rollout (safety net)
|
# Kill any background processes from this rollout (safety net)
|
||||||
try:
|
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)
|
killed = process_registry.kill_all(task_id=self.task_id)
|
||||||
if killed:
|
if killed:
|
||||||
logger.debug("Process cleanup for task %s: killed %d process(es)", self.task_id, killed)
|
logger.debug("Process cleanup for task %s: killed %d process(es)", self.task_id, killed)
|
||||||
|
|||||||
2
hermes
2
hermes
@@ -7,5 +7,5 @@ subcommands such as `gateway`, `cron`, and `doctor`.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from hermes_cli.main import main
|
from hermes_agent.cli.main import main
|
||||||
main()
|
main()
|
||||||
|
|||||||
0
hermes_agent/__init__.py
Normal file
0
hermes_agent/__init__.py
Normal file
@@ -8,7 +8,7 @@ from typing import Optional
|
|||||||
def detect_provider() -> Optional[str]:
|
def detect_provider() -> Optional[str]:
|
||||||
"""Resolve the active Hermes runtime provider, or None if unavailable."""
|
"""Resolve the active Hermes runtime provider, or None if unavailable."""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.runtime_provider import resolve_runtime_provider
|
from hermes_agent.cli.runtime_provider import resolve_runtime_provider
|
||||||
runtime = resolve_runtime_provider()
|
runtime = resolve_runtime_provider()
|
||||||
api_key = runtime.get("api_key")
|
api_key = runtime.get("api_key")
|
||||||
provider = runtime.get("provider")
|
provider = runtime.get("provider")
|
||||||
@@ -17,7 +17,7 @@ import asyncio
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
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
|
# 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:
|
def _load_env() -> None:
|
||||||
"""Load .env from HERMES_HOME (default ``~/.hermes``)."""
|
"""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()
|
hermes_home = get_hermes_home()
|
||||||
loaded = load_hermes_dotenv(hermes_home=hermes_home)
|
loaded = load_hermes_dotenv(hermes_home=hermes_home)
|
||||||
@@ -104,11 +104,6 @@ def main() -> None:
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logger.info("Starting hermes-agent ACP adapter")
|
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
|
import acp
|
||||||
from .server import HermesACPAgent
|
from .server import HermesACPAgent
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ def make_tool_progress_cb(
|
|||||||
snapshot = None
|
snapshot = None
|
||||||
if name in {"write_file", "patch", "skill_manage"}:
|
if name in {"write_file", "patch", "skill_manage"}:
|
||||||
try:
|
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)
|
snapshot = capture_local_edit_snapshot(name, args)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -52,20 +52,20 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
from acp.schema import AuthMethod as AuthMethodAgent # type: ignore[attr-defined]
|
from acp.schema import AuthMethod as AuthMethodAgent # type: ignore[attr-defined]
|
||||||
|
|
||||||
from acp_adapter.auth import detect_provider
|
from hermes_agent.acp.auth import detect_provider
|
||||||
from acp_adapter.events import (
|
from hermes_agent.acp.events import (
|
||||||
make_message_cb,
|
make_message_cb,
|
||||||
make_step_cb,
|
make_step_cb,
|
||||||
make_thinking_cb,
|
make_thinking_cb,
|
||||||
make_tool_progress_cb,
|
make_tool_progress_cb,
|
||||||
)
|
)
|
||||||
from acp_adapter.permissions import make_approval_callback
|
from hermes_agent.acp.permissions import make_approval_callback
|
||||||
from acp_adapter.session import SessionManager, SessionState
|
from hermes_agent.acp.session import SessionManager, SessionState
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from hermes_cli import __version__ as HERMES_VERSION
|
from hermes_agent.cli import __version__ as HERMES_VERSION
|
||||||
except Exception:
|
except Exception:
|
||||||
HERMES_VERSION = "0.0.0"
|
HERMES_VERSION = "0.0.0"
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ class HermesACPAgent(acp.Agent):
|
|||||||
provider = getattr(state.agent, "provider", None) or detect_provider() or "openrouter"
|
provider = getattr(state.agent, "provider", None) or detect_provider() or "openrouter"
|
||||||
|
|
||||||
try:
|
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)
|
normalized_provider = normalize_provider(provider)
|
||||||
provider_name = provider_label(normalized_provider)
|
provider_name = provider_label(normalized_provider)
|
||||||
@@ -235,7 +235,7 @@ class HermesACPAgent(acp.Agent):
|
|||||||
new_model = raw_model.strip()
|
new_model = raw_model.strip()
|
||||||
|
|
||||||
try:
|
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)
|
target_provider, new_model = parse_model_input(new_model, current_provider)
|
||||||
if target_provider == current_provider:
|
if target_provider == current_provider:
|
||||||
@@ -257,7 +257,7 @@ class HermesACPAgent(acp.Agent):
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from tools.mcp_tool import register_mcp_servers
|
from hermes_agent.tools.mcp.tool import register_mcp_servers
|
||||||
|
|
||||||
config_map: dict[str, dict] = {}
|
config_map: dict[str, dict] = {}
|
||||||
for server in mcp_servers:
|
for server in mcp_servers:
|
||||||
@@ -285,7 +285,7 @@ class HermesACPAgent(acp.Agent):
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
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"]
|
enabled_toolsets = getattr(state.agent, "enabled_toolsets", None) or ["hermes-acp"]
|
||||||
disabled_toolsets = getattr(state.agent, "disabled_toolsets", None)
|
disabled_toolsets = getattr(state.agent, "disabled_toolsets", None)
|
||||||
@@ -572,7 +572,7 @@ class HermesACPAgent(acp.Agent):
|
|||||||
nonlocal previous_approval_cb, previous_interactive
|
nonlocal previous_approval_cb, previous_interactive
|
||||||
if approval_cb:
|
if approval_cb:
|
||||||
try:
|
try:
|
||||||
from tools import terminal_tool as _terminal_tool
|
from hermes_agent.tools import terminal as _terminal_tool
|
||||||
previous_approval_cb = _terminal_tool._get_approval_callback()
|
previous_approval_cb = _terminal_tool._get_approval_callback()
|
||||||
_terminal_tool.set_approval_callback(approval_cb)
|
_terminal_tool.set_approval_callback(approval_cb)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -599,7 +599,7 @@ class HermesACPAgent(acp.Agent):
|
|||||||
os.environ["HERMES_INTERACTIVE"] = previous_interactive
|
os.environ["HERMES_INTERACTIVE"] = previous_interactive
|
||||||
if approval_cb:
|
if approval_cb:
|
||||||
try:
|
try:
|
||||||
from tools import terminal_tool as _terminal_tool
|
from hermes_agent.tools import terminal as _terminal_tool
|
||||||
_terminal_tool.set_approval_callback(previous_approval_cb)
|
_terminal_tool.set_approval_callback(previous_approval_cb)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.debug("Could not restore approval callback", exc_info=True)
|
logger.debug("Could not restore approval callback", exc_info=True)
|
||||||
@@ -618,7 +618,7 @@ class HermesACPAgent(acp.Agent):
|
|||||||
final_response = result.get("final_response", "")
|
final_response = result.get("final_response", "")
|
||||||
if final_response:
|
if final_response:
|
||||||
try:
|
try:
|
||||||
from agent.title_generator import maybe_auto_title
|
from hermes_agent.agent.title_generator import maybe_auto_title
|
||||||
|
|
||||||
maybe_auto_title(
|
maybe_auto_title(
|
||||||
self.session_manager._get_db(),
|
self.session_manager._get_db(),
|
||||||
@@ -753,7 +753,7 @@ class HermesACPAgent(acp.Agent):
|
|||||||
|
|
||||||
def _cmd_tools(self, args: str, state: SessionState) -> str:
|
def _cmd_tools(self, args: str, state: SessionState) -> str:
|
||||||
try:
|
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"]
|
toolsets = getattr(state.agent, "enabled_toolsets", None) or ["hermes-acp"]
|
||||||
tools = get_tool_definitions(enabled_toolsets=toolsets, quiet_mode=True)
|
tools = get_tool_definitions(enabled_toolsets=toolsets, quiet_mode=True)
|
||||||
if not tools:
|
if not tools:
|
||||||
@@ -804,7 +804,7 @@ class HermesACPAgent(acp.Agent):
|
|||||||
if not hasattr(agent, "_compress_context"):
|
if not hasattr(agent, "_compress_context"):
|
||||||
return "Context compression not available for this agent."
|
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)
|
original_count = len(state.history)
|
||||||
approx_tokens = estimate_messages_tokens_rough(state.history)
|
approx_tokens = estimate_messages_tokens_rough(state.history)
|
||||||
@@ -8,7 +8,7 @@ history.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_agent.constants import get_hermes_home
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
@@ -100,7 +100,7 @@ def _register_task_cwd(task_id: str, cwd: str) -> None:
|
|||||||
if not task_id:
|
if not task_id:
|
||||||
return
|
return
|
||||||
try:
|
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})
|
register_task_env_overrides(task_id, {"cwd": cwd})
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.debug("Failed to register ACP task cwd override", exc_info=True)
|
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:
|
if not task_id:
|
||||||
return
|
return
|
||||||
try:
|
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)
|
clear_task_env_overrides(task_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.debug("Failed to clear ACP task cwd override", exc_info=True)
|
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:
|
if self._db_instance is not None:
|
||||||
return self._db_instance
|
return self._db_instance
|
||||||
try:
|
try:
|
||||||
from hermes_state import SessionDB
|
from hermes_agent.state import SessionDB
|
||||||
hermes_home = get_hermes_home()
|
hermes_home = get_hermes_home()
|
||||||
self._db_instance = SessionDB(db_path=hermes_home / "state.db")
|
self._db_instance = SessionDB(db_path=hermes_home / "state.db")
|
||||||
return self._db_instance
|
return self._db_instance
|
||||||
@@ -523,9 +523,9 @@ class SessionManager:
|
|||||||
if self._agent_factory is not None:
|
if self._agent_factory is not None:
|
||||||
return self._agent_factory()
|
return self._agent_factory()
|
||||||
|
|
||||||
from run_agent import AIAgent
|
from hermes_agent.agent.loop import AIAgent
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
from hermes_cli.runtime_provider import resolve_runtime_provider
|
from hermes_agent.cli.runtime_provider import resolve_runtime_provider
|
||||||
|
|
||||||
config = load_config()
|
config = load_config()
|
||||||
model_cfg = config.get("model")
|
model_cfg = config.get("model")
|
||||||
@@ -103,7 +103,7 @@ def _build_patch_mode_content(patch_text: str) -> List[Any]:
|
|||||||
return [acp.tool_content(acp.text_block(""))]
|
return [acp.tool_content(acp.text_block(""))]
|
||||||
|
|
||||||
try:
|
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)
|
operations, error = parse_v4a_patch(patch_text)
|
||||||
if error or not operations:
|
if error or not operations:
|
||||||
@@ -243,7 +243,7 @@ def _build_tool_complete_content(
|
|||||||
|
|
||||||
if tool_name in {"write_file", "patch", "skill_manage"}:
|
if tool_name in {"write_file", "patch", "skill_manage"}:
|
||||||
try:
|
try:
|
||||||
from agent.display import extract_edit_diff
|
from hermes_agent.agent.display import extract_edit_diff
|
||||||
|
|
||||||
diff_text = extract_edit_diff(
|
diff_text = extract_edit_diff(
|
||||||
tool_name,
|
tool_name,
|
||||||
0
hermes_agent/agent/context/__init__.py
Normal file
0
hermes_agent/agent/context/__init__.py
Normal file
@@ -24,14 +24,14 @@ import re
|
|||||||
import time
|
import time
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from agent.auxiliary_client import call_llm
|
from hermes_agent.providers.auxiliary import call_llm
|
||||||
from agent.context_engine import ContextEngine
|
from hermes_agent.agent.context.engine import ContextEngine
|
||||||
from agent.model_metadata import (
|
from hermes_agent.providers.metadata import (
|
||||||
MINIMUM_CONTEXT_LENGTH,
|
MINIMUM_CONTEXT_LENGTH,
|
||||||
get_model_context_length,
|
get_model_context_length,
|
||||||
estimate_messages_tokens_rough,
|
estimate_messages_tokens_rough,
|
||||||
)
|
)
|
||||||
from agent.redact import redact_sensitive_text
|
from hermes_agent.agent.redact import redact_sensitive_text
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ from dataclasses import dataclass, field
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Awaitable, Callable
|
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]+\')'
|
_QUOTED_REFERENCE_VALUE = r'(?:`[^`\n]+`|"[^"\n]+"|\'[^\'\n]+\')'
|
||||||
REFERENCE_PATTERN = re.compile(
|
REFERENCE_PATTERN = re.compile(
|
||||||
@@ -315,7 +315,7 @@ async def _fetch_url_content(
|
|||||||
|
|
||||||
|
|
||||||
async def _default_url_fetcher(url: str) -> str:
|
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)
|
raw = await web_extract_tool([url], format="markdown", use_llm_processing=True)
|
||||||
payload = json.loads(raw)
|
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:
|
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()
|
home = Path(os.path.expanduser("~")).resolve()
|
||||||
hermes_home = get_hermes_home().resolve()
|
hermes_home = get_hermes_home().resolve()
|
||||||
|
|
||||||
@@ -21,8 +21,8 @@ from pathlib import Path
|
|||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from agent.file_safety import get_read_block_error, is_write_denied
|
from hermes_agent.agent.file_safety import get_read_block_error, is_write_denied
|
||||||
from agent.redact import redact_sensitive_text
|
from hermes_agent.agent.redact import redact_sensitive_text
|
||||||
|
|
||||||
ACP_MARKER_BASE_URL = "acp://copilot"
|
ACP_MARKER_BASE_URL = "acp://copilot"
|
||||||
_DEFAULT_TIMEOUT_SECONDS = 900.0
|
_DEFAULT_TIMEOUT_SECONDS = 900.0
|
||||||
@@ -13,7 +13,7 @@ from dataclasses import dataclass, field
|
|||||||
from difflib import unified_diff
|
from difflib import unified_diff
|
||||||
from pathlib import Path
|
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
|
# ANSI escape codes for coloring tool failure indicators
|
||||||
_RED = "\033[31m"
|
_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"
|
plus = "\033[38;2;255;255;255;48;2;20;90;20m"
|
||||||
|
|
||||||
try:
|
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()
|
skin = get_active_skin()
|
||||||
|
|
||||||
def _hex_fg(key: str, fallback_rgb: tuple[int, int, int]) -> str:
|
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():
|
def _get_skin():
|
||||||
"""Get the active skin config, or None if not available."""
|
"""Get the active skin config, or None if not available."""
|
||||||
try:
|
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()
|
return get_active_skin()
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
@@ -148,7 +148,7 @@ def get_tool_emoji(tool_name: str, default: str = "⚡") -> str:
|
|||||||
return override
|
return override
|
||||||
# 2. Registry default
|
# 2. Registry default
|
||||||
try:
|
try:
|
||||||
from tools.registry import registry
|
from hermes_agent.tools.registry import registry
|
||||||
emoji = registry.get_emoji(tool_name, default="")
|
emoji = registry.get_emoji(tool_name, default="")
|
||||||
if emoji:
|
if emoji:
|
||||||
return emoji
|
return emoji
|
||||||
@@ -311,7 +311,7 @@ def _resolve_skill_manage_paths(args: dict) -> list[Path]:
|
|||||||
if not action or not name:
|
if not action or not name:
|
||||||
return []
|
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":
|
if action == "create":
|
||||||
skill_dir = _resolve_skill_dir(name, args.get("category"))
|
skill_dir = _resolve_skill_dir(name, args.get("category"))
|
||||||
@@ -729,6 +729,7 @@ class KawaiiSpinner:
|
|||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
continue
|
continue
|
||||||
frame = self.spinner_frames[self.frame_idx % len(self.spinner_frames)]
|
frame = self.spinner_frames[self.frame_idx % len(self.spinner_frames)]
|
||||||
|
assert self.start_time is not None # start() sets it before thread starts
|
||||||
elapsed = time.time() - self.start_time
|
elapsed = time.time() - self.start_time
|
||||||
if wings:
|
if wings:
|
||||||
left, right = wings[self.frame_idx % len(wings)]
|
left, right = wings[self.frame_idx % len(wings)]
|
||||||
@@ -10,7 +10,7 @@ from typing import Optional
|
|||||||
def _hermes_home_path() -> Path:
|
def _hermes_home_path() -> Path:
|
||||||
"""Resolve the active HERMES_HOME (profile-aware) without circular imports."""
|
"""Resolve the active HERMES_HOME (profile-aware) without circular imports."""
|
||||||
try:
|
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()
|
return get_hermes_home()
|
||||||
except Exception:
|
except Exception:
|
||||||
return Path(os.path.expanduser("~/.hermes"))
|
return Path(os.path.expanduser("~/.hermes"))
|
||||||
0
hermes_agent/agent/image_gen/__init__.py
Normal file
0
hermes_agent/agent/image_gen/__init__.py
Normal file
@@ -164,7 +164,7 @@ def resolve_aspect_ratio(value: Optional[str]) -> str:
|
|||||||
|
|
||||||
def _images_cache_dir() -> Path:
|
def _images_cache_dir() -> Path:
|
||||||
"""Return ``$HERMES_HOME/cache/images/``, creating parents as needed."""
|
"""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 = get_hermes_home() / "cache" / "images"
|
||||||
path.mkdir(parents=True, exist_ok=True)
|
path.mkdir(parents=True, exist_ok=True)
|
||||||
@@ -24,7 +24,7 @@ import logging
|
|||||||
import threading
|
import threading
|
||||||
from typing import Dict, List, Optional
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ def get_active_provider() -> Optional[ImageGenProvider]:
|
|||||||
"""
|
"""
|
||||||
configured: Optional[str] = None
|
configured: Optional[str] = None
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
|
|
||||||
cfg = load_config()
|
cfg = load_config()
|
||||||
section = cfg.get("image_gen") if isinstance(cfg, dict) else None
|
section = cfg.get("image_gen") if isinstance(cfg, dict) else None
|
||||||
@@ -10,7 +10,7 @@ multi-platform architecture with additional cost estimation and platform
|
|||||||
breakdown capabilities.
|
breakdown capabilities.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
from agent.insights import InsightsEngine
|
from hermes_agent.agent.insights import InsightsEngine
|
||||||
engine = InsightsEngine(db)
|
engine = InsightsEngine(db)
|
||||||
report = engine.generate(days=30)
|
report = engine.generate(days=30)
|
||||||
print(engine.format_terminal(report))
|
print(engine.format_terminal(report))
|
||||||
@@ -22,7 +22,7 @@ from collections import Counter, defaultdict
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from agent.usage_pricing import (
|
from hermes_agent.providers.pricing import (
|
||||||
CanonicalUsage,
|
CanonicalUsage,
|
||||||
DEFAULT_PRICING,
|
DEFAULT_PRICING,
|
||||||
estimate_usage_cost,
|
estimate_usage_cost,
|
||||||
@@ -14,7 +14,7 @@ Features:
|
|||||||
- Support for multiple model providers
|
- Support for multiple model providers
|
||||||
|
|
||||||
Usage:
|
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")
|
agent = AIAgent(base_url="http://localhost:30000/v1", model="claude-opus-4-20250514")
|
||||||
response = agent.run_conversation("Tell me about the latest Python updates")
|
response = agent.run_conversation("Tell me about the latest Python updates")
|
||||||
@@ -37,18 +37,21 @@ import time
|
|||||||
import threading
|
import threading
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
import uuid
|
import uuid
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import Callable, List, Dict, Any, Optional, TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from hermes_agent.providers.rate_limiting import RateLimitState
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
import fire
|
import fire
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
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.
|
# Load .env from ~/.hermes/.env first, then project root as dev fallback.
|
||||||
# User-managed env files should override stale shell exports on restart.
|
# User-managed env files should override stale shell exports on restart.
|
||||||
from hermes_cli.env_loader import load_hermes_dotenv
|
from hermes_agent.cli.env_loader import load_hermes_dotenv
|
||||||
from hermes_cli.timeouts import (
|
from hermes_agent.cli.timeouts import (
|
||||||
get_provider_request_timeout,
|
get_provider_request_timeout,
|
||||||
get_provider_stale_timeout,
|
get_provider_stale_timeout,
|
||||||
)
|
)
|
||||||
@@ -64,30 +67,30 @@ else:
|
|||||||
|
|
||||||
|
|
||||||
# Import our tool system
|
# Import our tool system
|
||||||
from model_tools import (
|
from hermes_agent.tools.dispatch import (
|
||||||
get_tool_definitions,
|
get_tool_definitions,
|
||||||
get_toolset_for_tool,
|
get_toolset_for_tool,
|
||||||
handle_function_call,
|
handle_function_call,
|
||||||
check_toolset_requirements,
|
check_toolset_requirements,
|
||||||
)
|
)
|
||||||
from tools.terminal_tool import cleanup_vm, get_active_env, is_persistent_env
|
from hermes_agent.tools.terminal import cleanup_vm, get_active_env, is_persistent_env
|
||||||
from tools.tool_result_storage import maybe_persist_tool_result, enforce_turn_budget
|
from hermes_agent.tools.result_storage import maybe_persist_tool_result, enforce_turn_budget
|
||||||
from tools.interrupt import set_interrupt as _set_interrupt
|
from hermes_agent.tools.interrupt import set_interrupt as _set_interrupt
|
||||||
from tools.browser_tool import cleanup_browser
|
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
|
# Agent internals extracted to agent/ package for modularity
|
||||||
from agent.memory_manager import build_memory_context_block, sanitize_context
|
from hermes_agent.agent.memory.manager import build_memory_context_block, sanitize_context
|
||||||
from agent.retry_utils import jittered_backoff
|
from hermes_agent.providers.retry import jittered_backoff
|
||||||
from agent.error_classifier import classify_api_error, FailoverReason
|
from hermes_agent.providers.errors import classify_api_error, FailoverReason
|
||||||
from agent.prompt_builder import (
|
from hermes_agent.agent.prompt_builder import (
|
||||||
DEFAULT_AGENT_IDENTITY, PLATFORM_HINTS,
|
DEFAULT_AGENT_IDENTITY, PLATFORM_HINTS,
|
||||||
MEMORY_GUIDANCE, SESSION_SEARCH_GUIDANCE, SKILLS_GUIDANCE,
|
MEMORY_GUIDANCE, SESSION_SEARCH_GUIDANCE, SKILLS_GUIDANCE,
|
||||||
build_nous_subscription_prompt,
|
build_nous_subscription_prompt,
|
||||||
)
|
)
|
||||||
from agent.model_metadata import (
|
from hermes_agent.providers.metadata import (
|
||||||
fetch_model_metadata,
|
fetch_model_metadata,
|
||||||
estimate_tokens_rough, estimate_messages_tokens_rough, estimate_request_tokens_rough,
|
estimate_tokens_rough, estimate_messages_tokens_rough, estimate_request_tokens_rough,
|
||||||
get_next_probe_tier, parse_context_limit_from_error,
|
get_next_probe_tier, parse_context_limit_from_error,
|
||||||
@@ -95,12 +98,12 @@ from agent.model_metadata import (
|
|||||||
save_context_length, is_local_endpoint,
|
save_context_length, is_local_endpoint,
|
||||||
query_ollama_num_ctx,
|
query_ollama_num_ctx,
|
||||||
)
|
)
|
||||||
from agent.context_compressor import ContextCompressor
|
from hermes_agent.agent.context.compressor import ContextCompressor
|
||||||
from agent.subdirectory_hints import SubdirectoryHintTracker
|
from hermes_agent.agent.subdirectory_hints import SubdirectoryHintTracker
|
||||||
from agent.prompt_caching import apply_anthropic_cache_control
|
from hermes_agent.providers.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 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 agent.usage_pricing import estimate_usage_cost, normalize_usage
|
from hermes_agent.providers.pricing import estimate_usage_cost, normalize_usage
|
||||||
from agent.codex_responses_adapter import (
|
from hermes_agent.providers.codex_adapter import (
|
||||||
_chat_content_to_responses_parts,
|
_chat_content_to_responses_parts,
|
||||||
_chat_messages_to_responses_input as _codex_chat_messages_to_responses_input,
|
_chat_messages_to_responses_input as _codex_chat_messages_to_responses_input,
|
||||||
_derive_responses_function_call_id as _codex_derive_responses_function_call_id,
|
_derive_responses_function_call_id as _codex_derive_responses_function_call_id,
|
||||||
@@ -114,17 +117,17 @@ from agent.codex_responses_adapter import (
|
|||||||
_split_responses_tool_id as _codex_split_responses_tool_id,
|
_split_responses_tool_id as _codex_split_responses_tool_id,
|
||||||
_summarize_user_message_for_log,
|
_summarize_user_message_for_log,
|
||||||
)
|
)
|
||||||
from agent.display import (
|
from hermes_agent.agent.display import (
|
||||||
KawaiiSpinner, build_tool_preview as _build_tool_preview,
|
KawaiiSpinner, build_tool_preview as _build_tool_preview,
|
||||||
get_cute_tool_message as _get_cute_tool_message_impl,
|
get_cute_tool_message as _get_cute_tool_message_impl,
|
||||||
_detect_tool_failure,
|
_detect_tool_failure,
|
||||||
get_tool_emoji as _get_tool_emoji,
|
get_tool_emoji as _get_tool_emoji,
|
||||||
)
|
)
|
||||||
from agent.trajectory import (
|
from hermes_agent.agent.trajectory import (
|
||||||
convert_scratchpad_to_think, has_incomplete_scratchpad,
|
convert_scratchpad_to_think, has_incomplete_scratchpad,
|
||||||
save_trajectory as _save_trajectory_to_file,
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -733,17 +736,17 @@ class AIAgent:
|
|||||||
provider_require_parameters: bool = False,
|
provider_require_parameters: bool = False,
|
||||||
provider_data_collection: str = None,
|
provider_data_collection: str = None,
|
||||||
session_id: str = None,
|
session_id: str = None,
|
||||||
tool_progress_callback: callable = None,
|
tool_progress_callback: Callable[..., Any] = None,
|
||||||
tool_start_callback: callable = None,
|
tool_start_callback: Callable[..., Any] = None,
|
||||||
tool_complete_callback: callable = None,
|
tool_complete_callback: Callable[..., Any] = None,
|
||||||
thinking_callback: callable = None,
|
thinking_callback: Callable[..., Any] = None,
|
||||||
reasoning_callback: callable = None,
|
reasoning_callback: Callable[..., Any] = None,
|
||||||
clarify_callback: callable = None,
|
clarify_callback: Callable[..., Any] = None,
|
||||||
step_callback: callable = None,
|
step_callback: Callable[..., Any] = None,
|
||||||
stream_delta_callback: callable = None,
|
stream_delta_callback: Callable[..., Any] = None,
|
||||||
interim_assistant_callback: callable = None,
|
interim_assistant_callback: Callable[..., Any] = None,
|
||||||
tool_gen_callback: callable = None,
|
tool_gen_callback: Callable[..., Any] = None,
|
||||||
status_callback: callable = None,
|
status_callback: Callable[..., Any] = None,
|
||||||
max_tokens: int = None,
|
max_tokens: int = None,
|
||||||
reasoning_config: Dict[str, Any] = None,
|
reasoning_config: Dict[str, Any] = None,
|
||||||
service_tier: str = None,
|
service_tier: str = None,
|
||||||
@@ -873,7 +876,7 @@ class AIAgent:
|
|||||||
self.api_mode = "chat_completions"
|
self.api_mode = "chat_completions"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from hermes_cli.model_normalize import (
|
from hermes_agent.cli.models.normalize import (
|
||||||
_AGGREGATOR_PROVIDERS,
|
_AGGREGATOR_PROVIDERS,
|
||||||
normalize_model_for_provider,
|
normalize_model_for_provider,
|
||||||
)
|
)
|
||||||
@@ -1023,7 +1026,7 @@ class AIAgent:
|
|||||||
# Centralized logging — agent.log (INFO+) and errors.log (WARNING+)
|
# Centralized logging — agent.log (INFO+) and errors.log (WARNING+)
|
||||||
# both live under ~/.hermes/logs/. Idempotent, so gateway mode
|
# both live under ~/.hermes/logs/. Idempotent, so gateway mode
|
||||||
# (which creates a new AIAgent per message) won't duplicate handlers.
|
# (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)
|
setup_logging(hermes_home=_hermes_home)
|
||||||
|
|
||||||
if self.verbose_logging:
|
if self.verbose_logging:
|
||||||
@@ -1037,10 +1040,10 @@ class AIAgent:
|
|||||||
# File handlers (agent.log, errors.log) still capture everything.
|
# File handlers (agent.log, errors.log) still capture everything.
|
||||||
for quiet_logger in [
|
for quiet_logger in [
|
||||||
'tools', # all tools.* (terminal, browser, web, file, etc.)
|
'tools', # all tools.* (terminal, browser, web, file, etc.)
|
||||||
'run_agent', # agent runner internals
|
'hermes_agent.agent.loop', # agent runner internals
|
||||||
'trajectory_compressor',
|
'scripts.trajectory_compressor',
|
||||||
'cron', # scheduler (only relevant in daemon mode)
|
'cron', # scheduler (only relevant in daemon mode)
|
||||||
'hermes_cli', # CLI helpers
|
'hermes_agent.cli', # CLI helpers
|
||||||
]:
|
]:
|
||||||
logging.getLogger(quiet_logger).setLevel(logging.ERROR)
|
logging.getLogger(quiet_logger).setLevel(logging.ERROR)
|
||||||
|
|
||||||
@@ -1082,12 +1085,12 @@ class AIAgent:
|
|||||||
_provider_timeout = get_provider_request_timeout(self.provider, self.model)
|
_provider_timeout = get_provider_request_timeout(self.provider, self.model)
|
||||||
|
|
||||||
if self.api_mode == "anthropic_messages":
|
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
|
# Bedrock + Claude → use AnthropicBedrock SDK for full feature parity
|
||||||
# (prompt caching, thinking budgets, adaptive thinking).
|
# (prompt caching, thinking budgets, adaptive thinking).
|
||||||
_is_bedrock_anthropic = self.provider == "bedrock"
|
_is_bedrock_anthropic = self.provider == "bedrock"
|
||||||
if _is_bedrock_anthropic:
|
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 "")
|
_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"
|
_br_region = _region_match.group(1) if _region_match else "us-east-1"
|
||||||
self._bedrock_region = _br_region
|
self._bedrock_region = _br_region
|
||||||
@@ -1116,7 +1119,7 @@ class AIAgent:
|
|||||||
# so injects Claude-Code identity headers and system prompts
|
# so injects Claude-Code identity headers and system prompts
|
||||||
# that cause 401/403 on their endpoints. Guards #1739 and
|
# that cause 401/403 on their endpoints. Guards #1739 and
|
||||||
# the third-party identity-injection bug.
|
# 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._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)
|
self._anthropic_client = build_anthropic_client(effective_key, base_url, timeout=_provider_timeout)
|
||||||
# No OpenAI client needed for Anthropic mode
|
# No OpenAI client needed for Anthropic mode
|
||||||
@@ -1134,7 +1137,7 @@ class AIAgent:
|
|||||||
# Guardrail config — read from config.yaml at init time.
|
# Guardrail config — read from config.yaml at init time.
|
||||||
self._bedrock_guardrail_config = None
|
self._bedrock_guardrail_config = None
|
||||||
try:
|
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", {})
|
_gr = _load_br_cfg().get("bedrock", {}).get("guardrail", {})
|
||||||
if _gr.get("guardrail_identifier") and _gr.get("guardrail_version"):
|
if _gr.get("guardrail_identifier") and _gr.get("guardrail_version"):
|
||||||
self._bedrock_guardrail_config = {
|
self._bedrock_guardrail_config = {
|
||||||
@@ -1170,7 +1173,7 @@ class AIAgent:
|
|||||||
"X-OpenRouter-Categories": "productivity,cli-agent",
|
"X-OpenRouter-Categories": "productivity,cli-agent",
|
||||||
}
|
}
|
||||||
elif base_url_host_matches(effective_base, "api.githubcopilot.com"):
|
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()
|
client_kwargs["default_headers"] = copilot_default_headers()
|
||||||
elif base_url_host_matches(effective_base, "api.kimi.com"):
|
elif base_url_host_matches(effective_base, "api.kimi.com"):
|
||||||
@@ -1180,11 +1183,11 @@ class AIAgent:
|
|||||||
elif base_url_host_matches(effective_base, "portal.qwen.ai"):
|
elif base_url_host_matches(effective_base, "portal.qwen.ai"):
|
||||||
client_kwargs["default_headers"] = _qwen_portal_headers()
|
client_kwargs["default_headers"] = _qwen_portal_headers()
|
||||||
elif base_url_host_matches(effective_base, "chatgpt.com"):
|
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)
|
client_kwargs["default_headers"] = _codex_cloudflare_headers(api_key)
|
||||||
else:
|
else:
|
||||||
# No explicit creds — use the centralized provider router
|
# 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(
|
_routed_client, _ = resolve_provider_client(
|
||||||
self.provider or "auto", model=self.model, raw_codex=True)
|
self.provider or "auto", model=self.model, raw_codex=True)
|
||||||
if _routed_client is not None:
|
if _routed_client is not None:
|
||||||
@@ -1208,7 +1211,7 @@ class AIAgent:
|
|||||||
# (e.g. alibaba → DASHSCOPE_API_KEY, not ALIBABA_API_KEY).
|
# (e.g. alibaba → DASHSCOPE_API_KEY, not ALIBABA_API_KEY).
|
||||||
_env_hint = f"{_explicit.upper()}_API_KEY"
|
_env_hint = f"{_explicit.upper()}_API_KEY"
|
||||||
try:
|
try:
|
||||||
from hermes_cli.auth import PROVIDER_REGISTRY
|
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
|
||||||
_pcfg = PROVIDER_REGISTRY.get(_explicit)
|
_pcfg = PROVIDER_REGISTRY.get(_explicit)
|
||||||
if _pcfg and _pcfg.api_key_env_vars:
|
if _pcfg and _pcfg.api_key_env_vars:
|
||||||
_env_hint = _pcfg.api_key_env_vars[0]
|
_env_hint = _pcfg.api_key_env_vars[0]
|
||||||
@@ -1361,7 +1364,7 @@ class AIAgent:
|
|||||||
self._cached_system_prompt: Optional[str] = None
|
self._cached_system_prompt: Optional[str] = None
|
||||||
|
|
||||||
# Filesystem checkpoint manager (transparent — not a tool)
|
# Filesystem checkpoint manager (transparent — not a tool)
|
||||||
from tools.checkpoint_manager import CheckpointManager
|
from hermes_agent.tools.checkpoint import CheckpointManager
|
||||||
self._checkpoint_mgr = CheckpointManager(
|
self._checkpoint_mgr = CheckpointManager(
|
||||||
enabled=checkpoints_enabled,
|
enabled=checkpoints_enabled,
|
||||||
max_snapshots=checkpoint_max_snapshots,
|
max_snapshots=checkpoint_max_snapshots,
|
||||||
@@ -1397,12 +1400,12 @@ class AIAgent:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# In-memory todo list for task planning (one per agent/session)
|
# 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()
|
self._todo_store = TodoStore()
|
||||||
|
|
||||||
# Load config once for memory, skills, and compression sections
|
# Load config once for memory, skills, and compression sections
|
||||||
try:
|
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()
|
_agent_cfg = _load_agent_config()
|
||||||
except Exception:
|
except Exception:
|
||||||
_agent_cfg = {}
|
_agent_cfg = {}
|
||||||
@@ -1427,7 +1430,7 @@ class AIAgent:
|
|||||||
self._memory_nudge_interval = int(mem_config.get("nudge_interval", 10))
|
self._memory_nudge_interval = int(mem_config.get("nudge_interval", 10))
|
||||||
self._memory_flush_min_turns = int(mem_config.get("flush_min_turns", 6))
|
self._memory_flush_min_turns = int(mem_config.get("flush_min_turns", 6))
|
||||||
if self._memory_enabled or self._user_profile_enabled:
|
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(
|
self._memory_store = MemoryStore(
|
||||||
memory_char_limit=mem_config.get("memory_char_limit", 2200),
|
memory_char_limit=mem_config.get("memory_char_limit", 2200),
|
||||||
user_char_limit=mem_config.get("user_char_limit", 1375),
|
user_char_limit=mem_config.get("user_char_limit", 1375),
|
||||||
@@ -1446,8 +1449,8 @@ class AIAgent:
|
|||||||
_mem_provider_name = mem_config.get("provider", "") if mem_config else ""
|
_mem_provider_name = mem_config.get("provider", "") if mem_config else ""
|
||||||
|
|
||||||
if _mem_provider_name:
|
if _mem_provider_name:
|
||||||
from agent.memory_manager import MemoryManager as _MemoryManager
|
from hermes_agent.agent.memory.manager import MemoryManager as _MemoryManager
|
||||||
from plugins.memory import load_memory_provider as _load_mem
|
from hermes_agent.plugins.memory import load_memory_provider as _load_mem
|
||||||
self._memory_manager = _MemoryManager()
|
self._memory_manager = _MemoryManager()
|
||||||
_mp = _load_mem(_mem_provider_name)
|
_mp = _load_mem(_mem_provider_name)
|
||||||
if _mp and _mp.is_available():
|
if _mp and _mp.is_available():
|
||||||
@@ -1476,7 +1479,7 @@ class AIAgent:
|
|||||||
_init_kwargs["gateway_session_key"] = self._gateway_session_key
|
_init_kwargs["gateway_session_key"] = self._gateway_session_key
|
||||||
# Profile identity for per-profile provider scoping
|
# Profile identity for per-profile provider scoping
|
||||||
try:
|
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()
|
_profile = get_active_profile_name()
|
||||||
_init_kwargs["agent_identity"] = _profile
|
_init_kwargs["agent_identity"] = _profile
|
||||||
_init_kwargs["agent_workspace"] = "hermes"
|
_init_kwargs["agent_workspace"] = "hermes"
|
||||||
@@ -1587,7 +1590,7 @@ class AIAgent:
|
|||||||
# Check custom_providers per-model context_length
|
# Check custom_providers per-model context_length
|
||||||
if _config_context_length is None:
|
if _config_context_length is None:
|
||||||
try:
|
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)
|
_custom_providers = get_compatible_custom_providers(_agent_cfg)
|
||||||
except Exception:
|
except Exception:
|
||||||
_custom_providers = _agent_cfg.get("custom_providers")
|
_custom_providers = _agent_cfg.get("custom_providers")
|
||||||
@@ -1638,7 +1641,7 @@ class AIAgent:
|
|||||||
if _engine_name != "compressor":
|
if _engine_name != "compressor":
|
||||||
# Try loading from plugins/context_engine/<name>/
|
# Try loading from plugins/context_engine/<name>/
|
||||||
try:
|
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)
|
_selected_engine = load_context_engine(_engine_name)
|
||||||
except Exception as _ce_load_err:
|
except Exception as _ce_load_err:
|
||||||
logger.debug("Context engine load from plugins/context_engine/: %s", _ce_load_err)
|
logger.debug("Context engine load from plugins/context_engine/: %s", _ce_load_err)
|
||||||
@@ -1646,7 +1649,7 @@ class AIAgent:
|
|||||||
# Try general plugin system as fallback
|
# Try general plugin system as fallback
|
||||||
if _selected_engine is None:
|
if _selected_engine is None:
|
||||||
try:
|
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()
|
_candidate = get_plugin_context_engine()
|
||||||
if _candidate and _candidate.name == _engine_name:
|
if _candidate and _candidate.name == _engine_name:
|
||||||
_selected_engine = _candidate
|
_selected_engine = _candidate
|
||||||
@@ -1663,7 +1666,7 @@ class AIAgent:
|
|||||||
if _selected_engine is not None:
|
if _selected_engine is not None:
|
||||||
self.context_compressor = _selected_engine
|
self.context_compressor = _selected_engine
|
||||||
# Resolve context_length for plugin engines — mirrors switch_model() path
|
# 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(
|
_plugin_ctx_len = get_model_context_length(
|
||||||
self.model,
|
self.model,
|
||||||
base_url=self.base_url,
|
base_url=self.base_url,
|
||||||
@@ -1699,7 +1702,7 @@ class AIAgent:
|
|||||||
|
|
||||||
# Reject models whose context window is below the minimum required
|
# Reject models whose context window is below the minimum required
|
||||||
# for reliable tool-calling workflows (64K tokens).
|
# 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)
|
_ctx = getattr(self.context_compressor, "context_length", 0)
|
||||||
if _ctx and _ctx < MINIMUM_CONTEXT_LENGTH:
|
if _ctx and _ctx < MINIMUM_CONTEXT_LENGTH:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
@@ -1876,7 +1879,7 @@ class AIAgent:
|
|||||||
change persists across turns (unlike fallback which is
|
change persists across turns (unlike fallback which is
|
||||||
turn-scoped).
|
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 ──
|
# ── Determine api_mode if not provided ──
|
||||||
if not api_mode:
|
if not api_mode:
|
||||||
@@ -1908,7 +1911,7 @@ class AIAgent:
|
|||||||
|
|
||||||
# ── Build new client ──
|
# ── Build new client ──
|
||||||
if api_mode == "anthropic_messages":
|
if api_mode == "anthropic_messages":
|
||||||
from agent.anthropic_adapter import (
|
from hermes_agent.providers.anthropic_adapter import (
|
||||||
build_anthropic_client,
|
build_anthropic_client,
|
||||||
resolve_anthropic_token,
|
resolve_anthropic_token,
|
||||||
_is_oauth_token,
|
_is_oauth_token,
|
||||||
@@ -1956,7 +1959,7 @@ class AIAgent:
|
|||||||
|
|
||||||
# ── Update context compressor ──
|
# ── Update context compressor ──
|
||||||
if hasattr(self, "context_compressor") and self.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(
|
new_context_length = get_model_context_length(
|
||||||
self.model,
|
self.model,
|
||||||
base_url=self.base_url,
|
base_url=self.base_url,
|
||||||
@@ -2151,8 +2154,8 @@ class AIAgent:
|
|||||||
if not self.compression_enabled:
|
if not self.compression_enabled:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
from agent.auxiliary_client import get_text_auxiliary_client
|
from hermes_agent.providers.auxiliary import get_text_auxiliary_client
|
||||||
from agent.model_metadata import (
|
from hermes_agent.providers.metadata import (
|
||||||
MINIMUM_CONTEXT_LENGTH,
|
MINIMUM_CONTEXT_LENGTH,
|
||||||
get_model_context_length,
|
get_model_context_length,
|
||||||
)
|
)
|
||||||
@@ -2445,7 +2448,7 @@ class AIAgent:
|
|||||||
normalized_provider = (provider or "").strip().lower()
|
normalized_provider = (provider or "").strip().lower()
|
||||||
if normalized_provider == "copilot":
|
if normalized_provider == "copilot":
|
||||||
try:
|
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)
|
return _should_use_copilot_responses_api(model)
|
||||||
except Exception:
|
except Exception:
|
||||||
# Fall back to the generic GPT-5 rule if Copilot-specific
|
# Fall back to the generic GPT-5 rule if Copilot-specific
|
||||||
@@ -3753,7 +3756,7 @@ class AIAgent:
|
|||||||
if not headers:
|
if not headers:
|
||||||
return
|
return
|
||||||
try:
|
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)
|
state = parse_rate_limit_headers(headers, provider=self.provider)
|
||||||
if state is not None:
|
if state is not None:
|
||||||
self._rate_limit_state = state
|
self._rate_limit_state = state
|
||||||
@@ -3885,7 +3888,7 @@ class AIAgent:
|
|||||||
|
|
||||||
# 1. Kill background processes for this task
|
# 1. Kill background processes for this task
|
||||||
try:
|
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)
|
process_registry.kill_all(task_id=task_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -4102,7 +4105,7 @@ class AIAgent:
|
|||||||
if context_files_prompt:
|
if context_files_prompt:
|
||||||
prompt_parts.append(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()
|
now = _hermes_now()
|
||||||
timestamp_line = f"Conversation started: {now.strftime('%A, %B %d, %Y %I:%M %p')}"
|
timestamp_line = f"Conversation started: {now.strftime('%A, %B %d, %Y %I:%M %p')}"
|
||||||
if self.pass_session_id and self.session_id:
|
if self.pass_session_id and self.session_id:
|
||||||
@@ -4230,7 +4233,7 @@ class AIAgent:
|
|||||||
|
|
||||||
Returns the original list if no truncation was needed.
|
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()
|
max_children = _get_max_concurrent_children()
|
||||||
delegate_count = sum(1 for tc in tool_calls if tc.function.name == "delegate_task")
|
delegate_count = sum(1 for tc in tool_calls if tc.function.name == "delegate_task")
|
||||||
if delegate_count <= max_children:
|
if delegate_count <= max_children:
|
||||||
@@ -4406,7 +4409,7 @@ class AIAgent:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def _create_openai_client(self, client_kwargs: dict, *, reason: str, shared: bool) -> Any:
|
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
|
# 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
|
# 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
|
# reused on subsequent requests. #10933 hit this by injecting an httpx.Client
|
||||||
@@ -4419,7 +4422,7 @@ class AIAgent:
|
|||||||
_validate_proxy_env_urls()
|
_validate_proxy_env_urls()
|
||||||
_validate_base_url(client_kwargs.get("base_url"))
|
_validate_base_url(client_kwargs.get("base_url"))
|
||||||
if self.provider == "copilot-acp" or str(client_kwargs.get("base_url", "")).startswith("acp://copilot"):
|
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)
|
client = CopilotACPClient(**client_kwargs)
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -4430,7 +4433,7 @@ class AIAgent:
|
|||||||
)
|
)
|
||||||
return client
|
return client
|
||||||
if self.provider == "google-gemini-cli" or str(client_kwargs.get("base_url", "")).startswith("cloudcode-pa://"):
|
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
|
# Strip OpenAI-specific kwargs the Gemini client doesn't accept
|
||||||
safe_kwargs = {
|
safe_kwargs = {
|
||||||
@@ -4446,7 +4449,7 @@ class AIAgent:
|
|||||||
)
|
)
|
||||||
return client
|
return client
|
||||||
if self.provider == "gemini":
|
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 "")
|
base_url = str(client_kwargs.get("base_url", "") or "")
|
||||||
if is_native_gemini_base_url(base_url):
|
if is_native_gemini_base_url(base_url):
|
||||||
@@ -4695,7 +4698,7 @@ class AIAgent:
|
|||||||
def _close_request_openai_client(self, client: Any, *, reason: str) -> None:
|
def _close_request_openai_client(self, client: Any, *, reason: str) -> None:
|
||||||
self._close_openai_client(client, reason=reason, shared=False)
|
self._close_openai_client(client, reason=reason, shared=False)
|
||||||
|
|
||||||
def _run_codex_stream(self, api_kwargs: dict, client: Any = None, on_first_delta: callable = None):
|
def _run_codex_stream(self, api_kwargs: dict, client: Any = None, on_first_delta: Callable[..., Any] = None):
|
||||||
"""Execute one streaming Responses API request and return the final response."""
|
"""Execute one streaming Responses API request and return the final response."""
|
||||||
import httpx as _httpx
|
import httpx as _httpx
|
||||||
|
|
||||||
@@ -4901,7 +4904,7 @@ class AIAgent:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
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)
|
creds = resolve_codex_runtime_credentials(force_refresh=force)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -4930,7 +4933,7 @@ class AIAgent:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
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(
|
creds = resolve_nous_runtime_credentials(
|
||||||
min_key_ttl_seconds=max(60, int(os.getenv("HERMES_NOUS_MIN_KEY_TTL_SECONDS", "1800"))),
|
min_key_ttl_seconds=max(60, int(os.getenv("HERMES_NOUS_MIN_KEY_TTL_SECONDS", "1800"))),
|
||||||
@@ -4969,7 +4972,7 @@ class AIAgent:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
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()
|
new_token = resolve_anthropic_token()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -5002,19 +5005,19 @@ class AIAgent:
|
|||||||
# Only treat as OAuth on native Anthropic; third-party endpoints using
|
# Only treat as OAuth on native Anthropic; third-party endpoints using
|
||||||
# the Anthropic protocol must not trip OAuth paths (#1739 & third-party
|
# the Anthropic protocol must not trip OAuth paths (#1739 & third-party
|
||||||
# identity-injection guard).
|
# 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
|
self._is_anthropic_oauth = _is_oauth_token(new_token) if self.provider == "anthropic" else False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _apply_client_headers_for_base_url(self, base_url: str) -> None:
|
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"):
|
if base_url_host_matches(base_url, "openrouter.ai"):
|
||||||
self._client_kwargs["default_headers"] = dict(_OR_HEADERS)
|
self._client_kwargs["default_headers"] = dict(_OR_HEADERS)
|
||||||
elif base_url_host_matches(base_url, "ai-gateway.vercel.sh"):
|
elif base_url_host_matches(base_url, "ai-gateway.vercel.sh"):
|
||||||
self._client_kwargs["default_headers"] = dict(_AI_GATEWAY_HEADERS)
|
self._client_kwargs["default_headers"] = dict(_AI_GATEWAY_HEADERS)
|
||||||
elif base_url_host_matches(base_url, "api.githubcopilot.com"):
|
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()
|
self._client_kwargs["default_headers"] = copilot_default_headers()
|
||||||
elif base_url_host_matches(base_url, "api.kimi.com"):
|
elif base_url_host_matches(base_url, "api.kimi.com"):
|
||||||
@@ -5022,7 +5025,7 @@ class AIAgent:
|
|||||||
elif base_url_host_matches(base_url, "portal.qwen.ai"):
|
elif base_url_host_matches(base_url, "portal.qwen.ai"):
|
||||||
self._client_kwargs["default_headers"] = _qwen_portal_headers()
|
self._client_kwargs["default_headers"] = _qwen_portal_headers()
|
||||||
elif base_url_host_matches(base_url, "chatgpt.com"):
|
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["default_headers"] = _codex_cloudflare_headers(
|
||||||
self._client_kwargs.get("api_key", "")
|
self._client_kwargs.get("api_key", "")
|
||||||
)
|
)
|
||||||
@@ -5034,7 +5037,7 @@ class AIAgent:
|
|||||||
runtime_base = getattr(entry, "runtime_base_url", None) or getattr(entry, "base_url", None) or self.base_url
|
runtime_base = getattr(entry, "runtime_base_url", None) or getattr(entry, "base_url", None) or self.base_url
|
||||||
|
|
||||||
if self.api_mode == "anthropic_messages":
|
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:
|
try:
|
||||||
self._anthropic_client.close()
|
self._anthropic_client.close()
|
||||||
@@ -5178,7 +5181,7 @@ class AIAgent:
|
|||||||
result["response"] = self._anthropic_messages_create(api_kwargs)
|
result["response"] = self._anthropic_messages_create(api_kwargs)
|
||||||
elif self.api_mode == "bedrock_converse":
|
elif self.api_mode == "bedrock_converse":
|
||||||
# Bedrock uses boto3 directly — no OpenAI client needed.
|
# Bedrock uses boto3 directly — no OpenAI client needed.
|
||||||
from agent.bedrock_adapter import (
|
from hermes_agent.providers.bedrock_adapter import (
|
||||||
_get_bedrock_runtime_client,
|
_get_bedrock_runtime_client,
|
||||||
normalize_converse_response,
|
normalize_converse_response,
|
||||||
)
|
)
|
||||||
@@ -5243,7 +5246,7 @@ class AIAgent:
|
|||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
if self.api_mode == "anthropic_messages":
|
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.close()
|
||||||
self._anthropic_client = build_anthropic_client(
|
self._anthropic_client = build_anthropic_client(
|
||||||
@@ -5275,7 +5278,7 @@ class AIAgent:
|
|||||||
# seed future retries.
|
# seed future retries.
|
||||||
try:
|
try:
|
||||||
if self.api_mode == "anthropic_messages":
|
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.close()
|
||||||
self._anthropic_client = build_anthropic_client(
|
self._anthropic_client = build_anthropic_client(
|
||||||
@@ -5391,7 +5394,7 @@ class AIAgent:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _interruptible_streaming_api_call(
|
def _interruptible_streaming_api_call(
|
||||||
self, api_kwargs: dict, *, on_first_delta: callable = None
|
self, api_kwargs: dict, *, on_first_delta: Callable[..., Any] = None
|
||||||
):
|
):
|
||||||
"""Streaming variant of _interruptible_api_call for real-time token delivery.
|
"""Streaming variant of _interruptible_api_call for real-time token delivery.
|
||||||
|
|
||||||
@@ -5436,7 +5439,7 @@ class AIAgent:
|
|||||||
|
|
||||||
def _bedrock_call():
|
def _bedrock_call():
|
||||||
try:
|
try:
|
||||||
from agent.bedrock_adapter import (
|
from hermes_agent.providers.bedrock_adapter import (
|
||||||
_get_bedrock_runtime_client,
|
_get_bedrock_runtime_client,
|
||||||
stream_converse_with_callbacks,
|
stream_converse_with_callbacks,
|
||||||
)
|
)
|
||||||
@@ -6016,7 +6019,7 @@ class AIAgent:
|
|||||||
if self._interrupt_requested:
|
if self._interrupt_requested:
|
||||||
try:
|
try:
|
||||||
if self.api_mode == "anthropic_messages":
|
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.close()
|
||||||
self._anthropic_client = build_anthropic_client(
|
self._anthropic_client = build_anthropic_client(
|
||||||
@@ -6126,7 +6129,7 @@ class AIAgent:
|
|||||||
# raw_codex=True because the main agent needs direct responses.stream()
|
# raw_codex=True because the main agent needs direct responses.stream()
|
||||||
# access for Codex providers.
|
# access for Codex providers.
|
||||||
try:
|
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
|
# Pass base_url and api_key from fallback config so custom
|
||||||
# endpoints (e.g. Ollama Cloud) resolve correctly instead of
|
# endpoints (e.g. Ollama Cloud) resolve correctly instead of
|
||||||
# falling through to OpenRouter defaults.
|
# falling through to OpenRouter defaults.
|
||||||
@@ -6147,7 +6150,7 @@ class AIAgent:
|
|||||||
fb_provider)
|
fb_provider)
|
||||||
return self._try_activate_fallback() # try next in chain
|
return self._try_activate_fallback() # try next in chain
|
||||||
try:
|
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)
|
fb_model = normalize_model_for_provider(fb_model, fb_provider)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -6190,7 +6193,7 @@ class AIAgent:
|
|||||||
|
|
||||||
if fb_api_mode == "anthropic_messages":
|
if fb_api_mode == "anthropic_messages":
|
||||||
# Build native Anthropic client instead of using OpenAI client
|
# 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 "")
|
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.api_key = effective_key
|
||||||
self._anthropic_api_key = effective_key
|
self._anthropic_api_key = effective_key
|
||||||
@@ -6243,7 +6246,7 @@ class AIAgent:
|
|||||||
# context window (e.g. 200K) instead of the fallback's (e.g. 32K),
|
# context window (e.g. 200K) instead of the fallback's (e.g. 32K),
|
||||||
# causing oversized sessions to overflow the fallback.
|
# causing oversized sessions to overflow the fallback.
|
||||||
if hasattr(self, 'context_compressor') and self.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
|
||||||
fb_context_length = get_model_context_length(
|
fb_context_length = get_model_context_length(
|
||||||
self.model, base_url=self.base_url,
|
self.model, base_url=self.base_url,
|
||||||
api_key=self.api_key, provider=self.provider,
|
api_key=self.api_key, provider=self.provider,
|
||||||
@@ -6304,7 +6307,7 @@ class AIAgent:
|
|||||||
|
|
||||||
# ── Rebuild client for the primary provider ──
|
# ── Rebuild client for the primary provider ──
|
||||||
if self.api_mode == "anthropic_messages":
|
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_api_key = rt["anthropic_api_key"]
|
||||||
self._anthropic_base_url = rt["anthropic_base_url"]
|
self._anthropic_base_url = rt["anthropic_base_url"]
|
||||||
self._anthropic_client = build_anthropic_client(
|
self._anthropic_client = build_anthropic_client(
|
||||||
@@ -6401,7 +6404,7 @@ class AIAgent:
|
|||||||
self.api_key = rt["api_key"]
|
self.api_key = rt["api_key"]
|
||||||
|
|
||||||
if self.api_mode == "anthropic_messages":
|
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_api_key = rt["anthropic_api_key"]
|
||||||
self._anthropic_base_url = rt["anthropic_base_url"]
|
self._anthropic_base_url = rt["anthropic_base_url"]
|
||||||
self._anthropic_client = build_anthropic_client(
|
self._anthropic_client = build_anthropic_client(
|
||||||
@@ -6484,7 +6487,7 @@ class AIAgent:
|
|||||||
|
|
||||||
description = ""
|
description = ""
|
||||||
try:
|
try:
|
||||||
from tools.vision_tools import vision_analyze_tool
|
from hermes_agent.tools.vision import vision_analyze_tool
|
||||||
|
|
||||||
result_json = asyncio.run(
|
result_json = asyncio.run(
|
||||||
vision_analyze_tool(image_url=vision_source, user_prompt=analysis_prompt)
|
vision_analyze_tool(image_url=vision_source, user_prompt=analysis_prompt)
|
||||||
@@ -6560,7 +6563,7 @@ class AIAgent:
|
|||||||
"""Return the cached AnthropicTransport instance (lazy singleton)."""
|
"""Return the cached AnthropicTransport instance (lazy singleton)."""
|
||||||
t = getattr(self, "_anthropic_transport", None)
|
t = getattr(self, "_anthropic_transport", None)
|
||||||
if t is None:
|
if t is None:
|
||||||
from agent.transports import get_transport
|
from hermes_agent.providers import get_transport
|
||||||
t = get_transport("anthropic_messages")
|
t = get_transport("anthropic_messages")
|
||||||
self._anthropic_transport = t
|
self._anthropic_transport = t
|
||||||
return t
|
return t
|
||||||
@@ -6569,7 +6572,7 @@ class AIAgent:
|
|||||||
"""Return the cached ResponsesApiTransport instance (lazy singleton)."""
|
"""Return the cached ResponsesApiTransport instance (lazy singleton)."""
|
||||||
t = getattr(self, "_codex_transport", None)
|
t = getattr(self, "_codex_transport", None)
|
||||||
if t is None:
|
if t is None:
|
||||||
from agent.transports import get_transport
|
from hermes_agent.providers import get_transport
|
||||||
t = get_transport("codex_responses")
|
t = get_transport("codex_responses")
|
||||||
self._codex_transport = t
|
self._codex_transport = t
|
||||||
return t
|
return t
|
||||||
@@ -6578,7 +6581,7 @@ class AIAgent:
|
|||||||
"""Return the cached ChatCompletionsTransport instance (lazy singleton)."""
|
"""Return the cached ChatCompletionsTransport instance (lazy singleton)."""
|
||||||
t = getattr(self, "_chat_completions_transport", None)
|
t = getattr(self, "_chat_completions_transport", None)
|
||||||
if t is None:
|
if t is None:
|
||||||
from agent.transports import get_transport
|
from hermes_agent.providers import get_transport
|
||||||
t = get_transport("chat_completions")
|
t = get_transport("chat_completions")
|
||||||
self._chat_completions_transport = t
|
self._chat_completions_transport = t
|
||||||
return t
|
return t
|
||||||
@@ -6587,7 +6590,7 @@ class AIAgent:
|
|||||||
"""Return the cached BedrockTransport instance (lazy singleton)."""
|
"""Return the cached BedrockTransport instance (lazy singleton)."""
|
||||||
t = getattr(self, "_bedrock_transport", None)
|
t = getattr(self, "_bedrock_transport", None)
|
||||||
if t is None:
|
if t is None:
|
||||||
from agent.transports import get_transport
|
from hermes_agent.providers import get_transport
|
||||||
t = get_transport("bedrock_converse")
|
t = get_transport("bedrock_converse")
|
||||||
self._bedrock_transport = t
|
self._bedrock_transport = t
|
||||||
return t
|
return t
|
||||||
@@ -6792,7 +6795,7 @@ class AIAgent:
|
|||||||
# Temperature: _fixed_temperature_for_model may return OMIT_TEMPERATURE
|
# Temperature: _fixed_temperature_for_model may return OMIT_TEMPERATURE
|
||||||
# sentinel (temperature omitted entirely), a numeric override, or None.
|
# sentinel (temperature omitted entirely), a numeric override, or None.
|
||||||
try:
|
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)
|
_ft = _fixed_temperature_for_model(self.model, self.base_url)
|
||||||
_omit_temp = _ft is OMIT_TEMPERATURE
|
_omit_temp = _ft is OMIT_TEMPERATURE
|
||||||
_fixed_temp = _ft if not _omit_temp else None
|
_fixed_temp = _ft if not _omit_temp else None
|
||||||
@@ -6819,7 +6822,7 @@ class AIAgent:
|
|||||||
_ant_max = None
|
_ant_max = None
|
||||||
if (_is_or or _is_nous) and "claude" in (self.model or "").lower():
|
if (_is_or or _is_nous) and "claude" in (self.model or "").lower():
|
||||||
try:
|
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)
|
_ant_max = _get_anthropic_max_output(self.model)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass # fail open — let the proxy pick its default
|
pass # fail open — let the proxy pick its default
|
||||||
@@ -6885,7 +6888,7 @@ class AIAgent:
|
|||||||
or base_url_host_matches(self._base_url_lower, "api.githubcopilot.com")
|
or base_url_host_matches(self._base_url_lower, "api.githubcopilot.com")
|
||||||
):
|
):
|
||||||
try:
|
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))
|
return bool(github_model_reasoning_efforts(self.model))
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -6909,7 +6912,7 @@ class AIAgent:
|
|||||||
def _github_models_reasoning_extra_body(self) -> dict | None:
|
def _github_models_reasoning_extra_body(self) -> dict | None:
|
||||||
"""Format reasoning payload for GitHub Models/OpenAI-compatible routes."""
|
"""Format reasoning payload for GitHub Models/OpenAI-compatible routes."""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.models import github_model_reasoning_efforts
|
from hermes_agent.cli.models.models import github_model_reasoning_efforts
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -7188,7 +7191,7 @@ class AIAgent:
|
|||||||
|
|
||||||
# Use auxiliary client for the flush call when available --
|
# Use auxiliary client for the flush call when available --
|
||||||
# it's cheaper and avoids Codex Responses API incompatibility.
|
# 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,
|
call_llm as _call_llm,
|
||||||
_fixed_temperature_for_model,
|
_fixed_temperature_for_model,
|
||||||
OMIT_TEMPERATURE,
|
OMIT_TEMPERATURE,
|
||||||
@@ -7205,12 +7208,15 @@ class AIAgent:
|
|||||||
_flush_temperature = _fixed_temp
|
_flush_temperature = _fixed_temp
|
||||||
else:
|
else:
|
||||||
_flush_temperature = 0.3
|
_flush_temperature = 0.3
|
||||||
|
_flush_llm_kwargs: dict = {}
|
||||||
|
if _flush_temperature is not None:
|
||||||
|
_flush_llm_kwargs["temperature"] = _flush_temperature
|
||||||
try:
|
try:
|
||||||
response = _call_llm(
|
response = _call_llm(
|
||||||
task="flush_memories",
|
task="flush_memories",
|
||||||
messages=api_messages,
|
messages=api_messages,
|
||||||
tools=[memory_tool_def],
|
tools=[memory_tool_def],
|
||||||
temperature=_flush_temperature,
|
**_flush_llm_kwargs,
|
||||||
max_tokens=5120,
|
max_tokens=5120,
|
||||||
# timeout resolved from auxiliary.flush_memories.timeout config
|
# timeout resolved from auxiliary.flush_memories.timeout config
|
||||||
)
|
)
|
||||||
@@ -7248,7 +7254,7 @@ class AIAgent:
|
|||||||
}
|
}
|
||||||
if _flush_temperature is not None:
|
if _flush_temperature is not None:
|
||||||
api_kwargs["temperature"] = _flush_temperature
|
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(
|
response = self._ensure_primary_openai_client(reason="flush_memories").chat.completions.create(
|
||||||
**api_kwargs, timeout=_get_task_timeout("flush_memories")
|
**api_kwargs, timeout=_get_task_timeout("flush_memories")
|
||||||
)
|
)
|
||||||
@@ -7285,7 +7291,7 @@ class AIAgent:
|
|||||||
try:
|
try:
|
||||||
args = json.loads(tc.function.arguments)
|
args = json.loads(tc.function.arguments)
|
||||||
flush_target = args.get("target", "memory")
|
flush_target = args.get("target", "memory")
|
||||||
from tools.memory_tool import memory_tool as _memory_tool
|
from hermes_agent.tools.memory import memory_tool as _memory_tool
|
||||||
_memory_tool(
|
_memory_tool(
|
||||||
action=args.get("action"),
|
action=args.get("action"),
|
||||||
target=flush_target,
|
target=flush_target,
|
||||||
@@ -7399,7 +7405,7 @@ class AIAgent:
|
|||||||
# read content is summarised away — if the model re-reads the same
|
# read content is summarised away — if the model re-reads the same
|
||||||
# file it needs the full content, not a "file unchanged" stub.
|
# file it needs the full content, not a "file unchanged" stub.
|
||||||
try:
|
try:
|
||||||
from tools.file_tools import reset_file_dedup
|
from hermes_agent.tools.files.tools import reset_file_dedup
|
||||||
reset_file_dedup(task_id)
|
reset_file_dedup(task_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -7440,7 +7446,7 @@ class AIAgent:
|
|||||||
New DELEGATE_TASK_SCHEMA fields only need to be added here to reach all
|
New DELEGATE_TASK_SCHEMA fields only need to be added here to reach all
|
||||||
invocation paths (concurrent, sequential, inline).
|
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(
|
return _delegate_task(
|
||||||
goal=function_args.get("goal"),
|
goal=function_args.get("goal"),
|
||||||
context=function_args.get("context"),
|
context=function_args.get("context"),
|
||||||
@@ -7464,7 +7470,7 @@ class AIAgent:
|
|||||||
# Check plugin hooks for a block directive before executing anything.
|
# Check plugin hooks for a block directive before executing anything.
|
||||||
block_message: Optional[str] = None
|
block_message: Optional[str] = None
|
||||||
try:
|
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(
|
block_message = get_pre_tool_call_block_message(
|
||||||
function_name, function_args, task_id=effective_task_id or "",
|
function_name, function_args, task_id=effective_task_id or "",
|
||||||
)
|
)
|
||||||
@@ -7474,7 +7480,7 @@ class AIAgent:
|
|||||||
return json.dumps({"error": block_message}, ensure_ascii=False)
|
return json.dumps({"error": block_message}, ensure_ascii=False)
|
||||||
|
|
||||||
if function_name == "todo":
|
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(
|
return _todo_tool(
|
||||||
todos=function_args.get("todos"),
|
todos=function_args.get("todos"),
|
||||||
merge=function_args.get("merge", False),
|
merge=function_args.get("merge", False),
|
||||||
@@ -7483,7 +7489,7 @@ class AIAgent:
|
|||||||
elif function_name == "session_search":
|
elif function_name == "session_search":
|
||||||
if not self._session_db:
|
if not self._session_db:
|
||||||
return json.dumps({"success": False, "error": "Session database not available."})
|
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(
|
return _session_search(
|
||||||
query=function_args.get("query", ""),
|
query=function_args.get("query", ""),
|
||||||
role_filter=function_args.get("role_filter"),
|
role_filter=function_args.get("role_filter"),
|
||||||
@@ -7493,7 +7499,7 @@ class AIAgent:
|
|||||||
)
|
)
|
||||||
elif function_name == "memory":
|
elif function_name == "memory":
|
||||||
target = function_args.get("target", "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(
|
result = _memory_tool(
|
||||||
action=function_args.get("action"),
|
action=function_args.get("action"),
|
||||||
target=target,
|
target=target,
|
||||||
@@ -7515,7 +7521,7 @@ class AIAgent:
|
|||||||
elif self._memory_manager and self._memory_manager.has_tool(function_name):
|
elif self._memory_manager and self._memory_manager.has_tool(function_name):
|
||||||
return self._memory_manager.handle_tool_call(function_name, function_args)
|
return self._memory_manager.handle_tool_call(function_name, function_args)
|
||||||
elif function_name == "clarify":
|
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(
|
return _clarify_tool(
|
||||||
question=function_args.get("question", ""),
|
question=function_args.get("question", ""),
|
||||||
choices=function_args.get("choices"),
|
choices=function_args.get("choices"),
|
||||||
@@ -7678,7 +7684,7 @@ class AIAgent:
|
|||||||
# The callback is thread-local; the main thread's callback
|
# The callback is thread-local; the main thread's callback
|
||||||
# is invisible to worker threads.
|
# is invisible to worker threads.
|
||||||
try:
|
try:
|
||||||
from tools.environments.base import set_activity_callback
|
from hermes_agent.backends.base import set_activity_callback
|
||||||
set_activity_callback(self._touch_activity)
|
set_activity_callback(self._touch_activity)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -7893,7 +7899,7 @@ class AIAgent:
|
|||||||
# Check plugin hooks for a block directive before executing.
|
# Check plugin hooks for a block directive before executing.
|
||||||
_block_msg: Optional[str] = None
|
_block_msg: Optional[str] = None
|
||||||
try:
|
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(
|
_block_msg = get_pre_tool_call_block_message(
|
||||||
function_name, function_args, task_id=effective_task_id or "",
|
function_name, function_args, task_id=effective_task_id or "",
|
||||||
)
|
)
|
||||||
@@ -7929,7 +7935,7 @@ class AIAgent:
|
|||||||
# the agent while a command is running.
|
# the agent while a command is running.
|
||||||
if _block_msg is None:
|
if _block_msg is None:
|
||||||
try:
|
try:
|
||||||
from tools.environments.base import set_activity_callback
|
from hermes_agent.backends.base import set_activity_callback
|
||||||
set_activity_callback(self._touch_activity)
|
set_activity_callback(self._touch_activity)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -7978,7 +7984,7 @@ class AIAgent:
|
|||||||
function_result = json.dumps({"error": _block_msg}, ensure_ascii=False)
|
function_result = json.dumps({"error": _block_msg}, ensure_ascii=False)
|
||||||
tool_duration = 0.0
|
tool_duration = 0.0
|
||||||
elif function_name == "todo":
|
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(
|
function_result = _todo_tool(
|
||||||
todos=function_args.get("todos"),
|
todos=function_args.get("todos"),
|
||||||
merge=function_args.get("merge", False),
|
merge=function_args.get("merge", False),
|
||||||
@@ -7991,7 +7997,7 @@ class AIAgent:
|
|||||||
if not self._session_db:
|
if not self._session_db:
|
||||||
function_result = json.dumps({"success": False, "error": "Session database not available."})
|
function_result = json.dumps({"success": False, "error": "Session database not available."})
|
||||||
else:
|
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(
|
function_result = _session_search(
|
||||||
query=function_args.get("query", ""),
|
query=function_args.get("query", ""),
|
||||||
role_filter=function_args.get("role_filter"),
|
role_filter=function_args.get("role_filter"),
|
||||||
@@ -8004,7 +8010,7 @@ class AIAgent:
|
|||||||
self._vprint(f" {_get_cute_tool_message_impl('session_search', function_args, tool_duration, result=function_result)}")
|
self._vprint(f" {_get_cute_tool_message_impl('session_search', function_args, tool_duration, result=function_result)}")
|
||||||
elif function_name == "memory":
|
elif function_name == "memory":
|
||||||
target = function_args.get("target", "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(
|
function_result = _memory_tool(
|
||||||
action=function_args.get("action"),
|
action=function_args.get("action"),
|
||||||
target=target,
|
target=target,
|
||||||
@@ -8026,7 +8032,7 @@ class AIAgent:
|
|||||||
if self._should_emit_quiet_tool_messages():
|
if self._should_emit_quiet_tool_messages():
|
||||||
self._vprint(f" {_get_cute_tool_message_impl('memory', function_args, tool_duration, result=function_result)}")
|
self._vprint(f" {_get_cute_tool_message_impl('memory', function_args, tool_duration, result=function_result)}")
|
||||||
elif function_name == "clarify":
|
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(
|
function_result = _clarify_tool(
|
||||||
question=function_args.get("question", ""),
|
question=function_args.get("question", ""),
|
||||||
choices=function_args.get("choices"),
|
choices=function_args.get("choices"),
|
||||||
@@ -8281,7 +8287,7 @@ class AIAgent:
|
|||||||
|
|
||||||
summary_extra_body = {}
|
summary_extra_body = {}
|
||||||
try:
|
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:
|
except Exception:
|
||||||
_fixed_temperature_for_model = None
|
_fixed_temperature_for_model = None
|
||||||
_OMIT_TEMP = None
|
_OMIT_TEMP = None
|
||||||
@@ -8418,9 +8424,9 @@ class AIAgent:
|
|||||||
self,
|
self,
|
||||||
user_message: str,
|
user_message: str,
|
||||||
system_message: str = None,
|
system_message: str = None,
|
||||||
conversation_history: List[Dict[str, Any]] = None,
|
conversation_history: List[Dict[str, Any]] | None = None,
|
||||||
task_id: str = None,
|
task_id: str = None,
|
||||||
stream_callback: Optional[callable] = None,
|
stream_callback: Optional[Callable[..., Any]] = None,
|
||||||
persist_user_message: Optional[str] = None,
|
persist_user_message: Optional[str] = None,
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
@@ -8448,7 +8454,7 @@ class AIAgent:
|
|||||||
|
|
||||||
# Tag all log records on this thread with the session ID so
|
# Tag all log records on this thread with the session ID so
|
||||||
# ``hermes logs --session <id>`` can filter a single conversation.
|
# ``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)
|
set_session_context(self.session_id)
|
||||||
|
|
||||||
# If the previous turn activated fallback, restore the primary
|
# If the previous turn activated fallback, restore the primary
|
||||||
@@ -8610,7 +8616,7 @@ class AIAgent:
|
|||||||
# continuation). Plugins can use this to initialise
|
# continuation). Plugins can use this to initialise
|
||||||
# session-scoped state (e.g. warm a memory cache).
|
# session-scoped state (e.g. warm a memory cache).
|
||||||
try:
|
try:
|
||||||
from hermes_cli.plugins import invoke_hook as _invoke_hook
|
from hermes_agent.cli.plugins import invoke_hook as _invoke_hook
|
||||||
_invoke_hook(
|
_invoke_hook(
|
||||||
"on_session_start",
|
"on_session_start",
|
||||||
session_id=self.session_id,
|
session_id=self.session_id,
|
||||||
@@ -8711,7 +8717,7 @@ class AIAgent:
|
|||||||
# All injected context is ephemeral (not persisted to session DB).
|
# All injected context is ephemeral (not persisted to session DB).
|
||||||
_plugin_user_context = ""
|
_plugin_user_context = ""
|
||||||
try:
|
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_results = _invoke_hook(
|
||||||
"pre_llm_call",
|
"pre_llm_call",
|
||||||
session_id=self.session_id,
|
session_id=self.session_id,
|
||||||
@@ -9085,7 +9091,7 @@ class AIAgent:
|
|||||||
# deepens the rate limit hole.
|
# deepens the rate limit hole.
|
||||||
if self.provider == "nous":
|
if self.provider == "nous":
|
||||||
try:
|
try:
|
||||||
from agent.nous_rate_guard import (
|
from hermes_agent.providers.nous_rate_guard import (
|
||||||
nous_rate_limit_remaining,
|
nous_rate_limit_remaining,
|
||||||
format_remaining as _fmt_nous_remaining,
|
format_remaining as _fmt_nous_remaining,
|
||||||
)
|
)
|
||||||
@@ -9134,7 +9140,7 @@ class AIAgent:
|
|||||||
api_kwargs = self._get_codex_transport().preflight_kwargs(api_kwargs, allow_stream=False)
|
api_kwargs = self._get_codex_transport().preflight_kwargs(api_kwargs, allow_stream=False)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from hermes_cli.plugins import invoke_hook as _invoke_hook
|
from hermes_agent.cli.plugins import invoke_hook as _invoke_hook
|
||||||
_invoke_hook(
|
_invoke_hook(
|
||||||
"pre_api_request",
|
"pre_api_request",
|
||||||
task_id=effective_task_id,
|
task_id=effective_task_id,
|
||||||
@@ -9759,7 +9765,7 @@ class AIAgent:
|
|||||||
# resume hitting Nous.
|
# resume hitting Nous.
|
||||||
if self.provider == "nous":
|
if self.provider == "nous":
|
||||||
try:
|
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()
|
clear_nous_rate_limit()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -10007,7 +10013,7 @@ class AIAgent:
|
|||||||
and not anthropic_auth_retry_attempted
|
and not anthropic_auth_retry_attempted
|
||||||
):
|
):
|
||||||
anthropic_auth_retry_attempted = True
|
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():
|
if self._try_refresh_anthropic_client_credentials():
|
||||||
print(f"{self.log_prefix}🔐 Anthropic credentials refreshed after 401. Retrying request...")
|
print(f"{self.log_prefix}🔐 Anthropic credentials refreshed after 401. Retrying request...")
|
||||||
continue
|
continue
|
||||||
@@ -10016,9 +10022,9 @@ class AIAgent:
|
|||||||
auth_method = "Bearer (OAuth/setup-token)" if _is_oauth_token(key) else "x-api-key (API key)"
|
auth_method = "Bearer (OAuth/setup-token)" if _is_oauth_token(key) else "x-api-key (API key)"
|
||||||
print(f"{self.log_prefix}🔐 Anthropic 401 — authentication failed.")
|
print(f"{self.log_prefix}🔐 Anthropic 401 — authentication failed.")
|
||||||
print(f"{self.log_prefix} Auth method: {auth_method}")
|
print(f"{self.log_prefix} Auth method: {auth_method}")
|
||||||
print(f"{self.log_prefix} Token prefix: {key[:12]}..." if key and len(key) > 12 else f"{self.log_prefix} Token: (empty or short)")
|
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:")
|
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()
|
_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_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")
|
print(f"{self.log_prefix} • Check ANTHROPIC_API_KEY in {_dhh}/.env for API keys or legacy token values")
|
||||||
@@ -10227,7 +10233,7 @@ class AIAgent:
|
|||||||
and not recovered_with_pool
|
and not recovered_with_pool
|
||||||
):
|
):
|
||||||
try:
|
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_resp = getattr(api_error, "response", None)
|
||||||
_err_hdrs = (
|
_err_hdrs = (
|
||||||
getattr(_err_resp, "headers", None)
|
getattr(_err_resp, "headers", None)
|
||||||
@@ -10770,7 +10776,7 @@ class AIAgent:
|
|||||||
assistant_message.content = str(raw)
|
assistant_message.content = str(raw)
|
||||||
|
|
||||||
try:
|
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_tool_calls = getattr(assistant_message, "tool_calls", None) or []
|
||||||
_assistant_text = assistant_message.content or ""
|
_assistant_text = assistant_message.content or ""
|
||||||
_invoke_hook(
|
_invoke_hook(
|
||||||
@@ -11416,7 +11422,7 @@ class AIAgent:
|
|||||||
messages.append(assistant_msg)
|
messages.append(assistant_msg)
|
||||||
|
|
||||||
if reasoning_text:
|
if reasoning_text:
|
||||||
reasoning_preview = reasoning_text[:500] + "..." if len(reasoning_text) > 500 else reasoning_text
|
reasoning_preview = str(reasoning_text)[:500] + "..." if len(str(reasoning_text)) > 500 else reasoning_text
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Reasoning-only response (no visible content) "
|
"Reasoning-only response (no visible content) "
|
||||||
"after exhausting retries and fallback. "
|
"after exhausting retries and fallback. "
|
||||||
@@ -11636,7 +11642,7 @@ class AIAgent:
|
|||||||
# to an external memory system).
|
# to an external memory system).
|
||||||
if final_response and not interrupted:
|
if final_response and not interrupted:
|
||||||
try:
|
try:
|
||||||
from hermes_cli.plugins import invoke_hook as _invoke_hook
|
from hermes_agent.cli.plugins import invoke_hook as _invoke_hook
|
||||||
_invoke_hook(
|
_invoke_hook(
|
||||||
"post_llm_call",
|
"post_llm_call",
|
||||||
session_id=self.session_id,
|
session_id=self.session_id,
|
||||||
@@ -11741,7 +11747,7 @@ class AIAgent:
|
|||||||
# Fired at the very end of every run_conversation call.
|
# Fired at the very end of every run_conversation call.
|
||||||
# Plugins can use this for cleanup, flushing buffers, etc.
|
# Plugins can use this for cleanup, flushing buffers, etc.
|
||||||
try:
|
try:
|
||||||
from hermes_cli.plugins import invoke_hook as _invoke_hook
|
from hermes_agent.cli.plugins import invoke_hook as _invoke_hook
|
||||||
_invoke_hook(
|
_invoke_hook(
|
||||||
"on_session_end",
|
"on_session_end",
|
||||||
session_id=self.session_id,
|
session_id=self.session_id,
|
||||||
@@ -11755,7 +11761,7 @@ class AIAgent:
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def chat(self, message: str, stream_callback: Optional[callable] = None) -> str:
|
def chat(self, message: str, stream_callback: Optional[Callable[..., Any]] = None) -> str:
|
||||||
"""
|
"""
|
||||||
Simple chat interface that returns just the final response.
|
Simple chat interface that returns just the final response.
|
||||||
|
|
||||||
@@ -11811,8 +11817,8 @@ def main(
|
|||||||
|
|
||||||
# Handle tool listing
|
# Handle tool listing
|
||||||
if list_tools:
|
if list_tools:
|
||||||
from model_tools import get_all_tool_names, get_available_toolsets
|
from hermes_agent.tools.dispatch import get_all_tool_names, get_available_toolsets
|
||||||
from toolsets import get_all_toolsets, get_toolset_info
|
from hermes_agent.tools.toolsets import get_all_toolsets, get_toolset_info
|
||||||
|
|
||||||
print("📋 Available Tools & Toolsets:")
|
print("📋 Available Tools & Toolsets:")
|
||||||
print("-" * 50)
|
print("-" * 50)
|
||||||
0
hermes_agent/agent/memory/__init__.py
Normal file
0
hermes_agent/agent/memory/__init__.py
Normal file
@@ -33,8 +33,8 @@ import logging
|
|||||||
import re
|
import re
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from agent.memory_provider import MemoryProvider
|
from hermes_agent.agent.memory.provider import MemoryProvider
|
||||||
from tools.registry import tool_error
|
from hermes_agent.tools.registry import tool_error
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -361,7 +361,7 @@ class MemoryManager:
|
|||||||
``get_hermes_home()`` themselves.
|
``get_hermes_home()`` themselves.
|
||||||
"""
|
"""
|
||||||
if "hermes_home" not in kwargs:
|
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())
|
kwargs["hermes_home"] = str(get_hermes_home())
|
||||||
for provider in self._providers:
|
for provider in self._providers:
|
||||||
try:
|
try:
|
||||||
@@ -12,10 +12,10 @@ import threading
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from pathlib import Path
|
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 typing import Optional
|
||||||
|
|
||||||
from agent.skill_utils import (
|
from hermes_agent.agent.skill_utils import (
|
||||||
extract_skill_conditions,
|
extract_skill_conditions,
|
||||||
extract_skill_description,
|
extract_skill_description,
|
||||||
get_all_skills_dirs,
|
get_all_skills_dirs,
|
||||||
@@ -24,7 +24,7 @@ from agent.skill_utils import (
|
|||||||
parse_frontmatter,
|
parse_frontmatter,
|
||||||
skill_matches_platform,
|
skill_matches_platform,
|
||||||
)
|
)
|
||||||
from utils import atomic_json_write
|
from hermes_agent.utils import atomic_json_write
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -619,7 +619,7 @@ def build_skills_system_prompt(
|
|||||||
# ── Layer 1: in-process LRU cache ─────────────────────────────────
|
# ── Layer 1: in-process LRU cache ─────────────────────────────────
|
||||||
# Include the resolved platform so per-platform disabled-skill lists
|
# Include the resolved platform so per-platform disabled-skill lists
|
||||||
# produce distinct cache entries (gateway serves multiple platforms).
|
# 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 = (
|
_platform_hint = (
|
||||||
os.environ.get("HERMES_PLATFORM")
|
os.environ.get("HERMES_PLATFORM")
|
||||||
or get_session_env("HERMES_SESSION_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:
|
def build_nous_subscription_prompt(valid_tool_names: "set[str] | None" = None) -> str:
|
||||||
"""Build a compact Nous subscription capability block for the system prompt."""
|
"""Build a compact Nous subscription capability block for the system prompt."""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.nous_subscription import get_nous_subscription_features
|
from hermes_agent.cli.nous_subscription import get_nous_subscription_features
|
||||||
from tools.tool_backend_helpers import managed_nous_tools_enabled
|
from hermes_agent.tools.backend_helpers import managed_nous_tools_enabled
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.debug("Failed to import Nous subscription helper: %s", exc)
|
logger.debug("Failed to import Nous subscription helper: %s", exc)
|
||||||
return ""
|
return ""
|
||||||
@@ -911,7 +911,7 @@ def load_soul_md() -> Optional[str]:
|
|||||||
``skip_soul=True`` so SOUL.md isn't injected twice.
|
``skip_soul=True`` so SOUL.md isn't injected twice.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import ensure_hermes_home
|
from hermes_agent.cli.config import ensure_hermes_home
|
||||||
ensure_hermes_home()
|
ensure_hermes_home()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug("Could not ensure HERMES_HOME before loading SOUL.md: %s", e)
|
logger.debug("Could not ensure HERMES_HOME before loading SOUL.md: %s", e)
|
||||||
@@ -75,7 +75,7 @@ try:
|
|||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
fcntl = None # type: ignore[assignment]
|
fcntl = None # type: ignore[assignment]
|
||||||
|
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_agent.constants import get_hermes_home
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ def register_from_config(
|
|||||||
registered: List[ShellHookSpec] = []
|
registered: List[ShellHookSpec] = []
|
||||||
|
|
||||||
# Import lazily — avoids circular imports at module-load time.
|
# 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()
|
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
|
Malformed entries warn-and-skip — we never raise from config parsing
|
||||||
because a broken hook must not crash the agent.
|
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):
|
if not isinstance(hooks_cfg, dict):
|
||||||
return []
|
return []
|
||||||
@@ -13,7 +13,7 @@ from datetime import datetime
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Optional
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ _INLINE_SHELL_MAX_OUTPUT = 4000
|
|||||||
def _load_skills_config() -> dict:
|
def _load_skills_config() -> dict:
|
||||||
"""Load the ``skills`` section of config.yaml (best-effort)."""
|
"""Load the ``skills`` section of config.yaml (best-effort)."""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
|
|
||||||
cfg = load_config() or {}
|
cfg = load_config() or {}
|
||||||
skills_cfg = cfg.get("skills")
|
skills_cfg = cfg.get("skills")
|
||||||
@@ -156,7 +156,7 @@ def _load_skill_payload(skill_identifier: str, task_id: str | None = None) -> tu
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
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()
|
identifier_path = Path(raw_identifier).expanduser()
|
||||||
if identifier_path.is_absolute():
|
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.
|
without needing to read config.yaml itself.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from agent.skill_utils import (
|
from hermes_agent.agent.skill_utils import (
|
||||||
extract_skill_config_vars,
|
extract_skill_config_vars,
|
||||||
parse_frontmatter,
|
parse_frontmatter,
|
||||||
resolve_skill_config_values,
|
resolve_skill_config_values,
|
||||||
@@ -241,7 +241,7 @@ def _build_skill_message(
|
|||||||
session_id: str | None = None,
|
session_id: str | None = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Format a loaded skill into a user/system message payload."""
|
"""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 "")
|
content = str(loaded_skill.get("content") or "")
|
||||||
|
|
||||||
@@ -344,8 +344,8 @@ def scan_skill_commands() -> Dict[str, Dict[str, Any]]:
|
|||||||
global _skill_commands
|
global _skill_commands
|
||||||
_skill_commands = {}
|
_skill_commands = {}
|
||||||
try:
|
try:
|
||||||
from tools.skills_tool import SKILLS_DIR, _parse_frontmatter, skill_matches_platform, _get_disabled_skill_names
|
from hermes_agent.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.agent.skill_utils import get_external_skills_dirs
|
||||||
disabled = _get_disabled_skill_names()
|
disabled = _get_disabled_skill_names()
|
||||||
seen_names: set = set()
|
seen_names: set = set()
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
from typing import Any, Dict, List, 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__)
|
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):
|
if not isinstance(skills_cfg, dict):
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
from gateway.session_context import get_session_env
|
from hermes_agent.gateway.session_context import get_session_env
|
||||||
resolved_platform = (
|
resolved_platform = (
|
||||||
platform
|
platform
|
||||||
or os.getenv("HERMES_PLATFORM")
|
or os.getenv("HERMES_PLATFORM")
|
||||||
@@ -455,7 +455,8 @@ def parse_qualified_name(name: str) -> Tuple[Optional[str], str]:
|
|||||||
"""
|
"""
|
||||||
if ":" not in name:
|
if ":" not in name:
|
||||||
return None, name
|
return None, name
|
||||||
return tuple(name.split(":", 1)) # type: ignore[return-value]
|
ns, bare = name.split(":", 1)
|
||||||
|
return ns, bare
|
||||||
|
|
||||||
|
|
||||||
def is_valid_namespace(candidate: Optional[str]) -> bool:
|
def is_valid_namespace(candidate: Optional[str]) -> bool:
|
||||||
@@ -19,7 +19,7 @@ import shlex
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, Optional, Set
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ import logging
|
|||||||
import threading
|
import threading
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from agent.auxiliary_client import call_llm
|
from hermes_agent.providers.auxiliary import call_llm
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -8,6 +8,6 @@ The terminal_tool.py factory (_create_environment) selects the backend
|
|||||||
based on the TERMINAL_ENV configuration.
|
based on the TERMINAL_ENV configuration.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from tools.environments.base import BaseEnvironment
|
from hermes_agent.backends.base import BaseEnvironment
|
||||||
|
|
||||||
__all__ = ["BaseEnvironment"]
|
__all__ = ["BaseEnvironment"]
|
||||||
@@ -20,8 +20,8 @@ from abc import ABC, abstractmethod
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import IO, Callable, Protocol
|
from typing import IO, Callable, Protocol
|
||||||
|
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_agent.constants import get_hermes_home
|
||||||
from tools.interrupt import is_interrupted
|
from hermes_agent.tools.interrupt import is_interrupted
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -245,7 +245,7 @@ class _ThreadedProcessHandle:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def wait(self, timeout: float | None = None) -> int:
|
def wait(self, timeout: float | None = None) -> int | None:
|
||||||
self._done.wait(timeout=timeout)
|
self._done.wait(timeout=timeout)
|
||||||
return self._returncode
|
return self._returncode
|
||||||
|
|
||||||
@@ -710,7 +710,7 @@ class BaseEnvironment(ABC):
|
|||||||
# server, `yes > /dev/null`, etc.), leaking the subshell forever.
|
# server, `yes > /dev/null`, etc.), leaking the subshell forever.
|
||||||
# Rewriting to `A && { B & }` runs B as a plain background in the
|
# Rewriting to `A && { B & }` runs B as a plain background in the
|
||||||
# current shell — no subshell wait.
|
# 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)
|
exec_command = _rewrite_compound_background(exec_command)
|
||||||
effective_timeout = timeout or self.timeout
|
effective_timeout = timeout or self.timeout
|
||||||
effective_cwd = cwd or self.cwd
|
effective_cwd = cwd or self.cwd
|
||||||
@@ -755,9 +755,9 @@ class BaseEnvironment(ABC):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _prepare_command(self, command: str) -> tuple[str, str | None]:
|
def _prepare_command(self, command: str) -> tuple[str | None, str | None]:
|
||||||
"""Transform sudo commands if SUDO_PASSWORD is available."""
|
"""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)
|
return _transform_sudo_command(command)
|
||||||
|
|
||||||
@@ -12,11 +12,11 @@ import shlex
|
|||||||
import threading
|
import threading
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from tools.environments.base import (
|
from hermes_agent.backends.base import (
|
||||||
BaseEnvironment,
|
BaseEnvironment,
|
||||||
_ThreadedProcessHandle,
|
_ThreadedProcessHandle,
|
||||||
)
|
)
|
||||||
from tools.environments.file_sync import (
|
from hermes_agent.backends.file_sync import (
|
||||||
FileSyncManager,
|
FileSyncManager,
|
||||||
iter_sync_files,
|
iter_sync_files,
|
||||||
quoted_mkdir_command,
|
quoted_mkdir_command,
|
||||||
@@ -14,8 +14,8 @@ import sys
|
|||||||
import uuid
|
import uuid
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from tools.environments.base import BaseEnvironment, _popen_bash
|
from hermes_agent.backends.base import BaseEnvironment, _popen_bash
|
||||||
from tools.environments.local import _HERMES_PROVIDER_ENV_BLOCKLIST
|
from hermes_agent.backends.local import _HERMES_PROVIDER_ENV_BLOCKLIST
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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]:
|
def _load_hermes_env_vars() -> dict[str, str]:
|
||||||
"""Load ~/.hermes/.env values without failing Docker command execution."""
|
"""Load ~/.hermes/.env values without failing Docker command execution."""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_env
|
from hermes_agent.cli.config import load_env
|
||||||
|
|
||||||
return load_env() or {}
|
return load_env() or {}
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -298,7 +298,7 @@ class DockerEnvironment(BaseEnvironment):
|
|||||||
# Persistent workspace via bind mounts from a configurable host directory
|
# Persistent workspace via bind mounts from a configurable host directory
|
||||||
# (TERMINAL_SANDBOX_DIR, default ~/.hermes/sandboxes/). Non-persistent
|
# (TERMINAL_SANDBOX_DIR, default ~/.hermes/sandboxes/). Non-persistent
|
||||||
# mode uses tmpfs (ephemeral, fast, gone on cleanup).
|
# 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)
|
# User-configured volume mounts (from config.yaml docker_volumes)
|
||||||
volume_args = []
|
volume_args = []
|
||||||
@@ -362,7 +362,7 @@ class DockerEnvironment(BaseEnvironment):
|
|||||||
# Mount credential files (OAuth tokens, etc.) declared by skills.
|
# Mount credential files (OAuth tokens, etc.) declared by skills.
|
||||||
# Read-only so the container can authenticate but not modify host creds.
|
# Read-only so the container can authenticate but not modify host creds.
|
||||||
try:
|
try:
|
||||||
from tools.credential_files import (
|
from hermes_agent.tools.credential_files import (
|
||||||
get_credential_file_mounts,
|
get_credential_file_mounts,
|
||||||
get_skills_directory_mount,
|
get_skills_directory_mount,
|
||||||
get_cache_directory_mounts,
|
get_cache_directory_mounts,
|
||||||
@@ -464,7 +464,7 @@ class DockerEnvironment(BaseEnvironment):
|
|||||||
explicit_forward_keys = set(self._forward_env)
|
explicit_forward_keys = set(self._forward_env)
|
||||||
passthrough_keys: set[str] = set()
|
passthrough_keys: set[str] = set()
|
||||||
try:
|
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())
|
passthrough_keys = set(get_all_passthrough())
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -24,8 +24,8 @@ except ImportError:
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_agent.constants import get_hermes_home
|
||||||
from tools.environments.base import _file_mtime_key
|
from hermes_agent.backends.base import _file_mtime_key
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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
|
# Late import: credential_files imports agent modules that create
|
||||||
# circular dependencies if loaded at file_sync module level.
|
# 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,
|
get_credential_file_mounts,
|
||||||
iter_cache_files,
|
iter_cache_files,
|
||||||
iter_skills_files,
|
iter_skills_files,
|
||||||
@@ -7,7 +7,7 @@ import signal
|
|||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from tools.environments.base import BaseEnvironment, _pipe_stdin
|
from hermes_agent.backends.base import BaseEnvironment, _pipe_stdin
|
||||||
|
|
||||||
_IS_WINDOWS = platform.system() == "Windows"
|
_IS_WINDOWS = platform.system() == "Windows"
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ def _build_provider_env_blocklist() -> frozenset:
|
|||||||
blocked: set[str] = set()
|
blocked: set[str] = set()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from hermes_cli.auth import PROVIDER_REGISTRY
|
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
|
||||||
for pconfig in PROVIDER_REGISTRY.values():
|
for pconfig in PROVIDER_REGISTRY.values():
|
||||||
blocked.update(pconfig.api_key_env_vars)
|
blocked.update(pconfig.api_key_env_vars)
|
||||||
if pconfig.base_url_env_var:
|
if pconfig.base_url_env_var:
|
||||||
@@ -30,7 +30,7 @@ def _build_provider_env_blocklist() -> frozenset:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
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():
|
for name, metadata in OPTIONAL_ENV_VARS.items():
|
||||||
category = metadata.get("category")
|
category = metadata.get("category")
|
||||||
if category in {"tool", "messaging"}:
|
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:
|
def _sanitize_subprocess_env(base_env: dict | None, extra_env: dict | None = None) -> dict:
|
||||||
"""Filter Hermes-managed secrets from a subprocess environment."""
|
"""Filter Hermes-managed secrets from a subprocess environment."""
|
||||||
try:
|
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:
|
except Exception:
|
||||||
_is_passthrough = lambda _: False # noqa: E731
|
_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
|
sanitized[key] = value
|
||||||
|
|
||||||
# Per-profile HOME isolation for background processes (same as _make_run_env).
|
# 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()
|
_profile_home = get_subprocess_home()
|
||||||
if _profile_home:
|
if _profile_home:
|
||||||
sanitized["HOME"] = _profile_home
|
sanitized["HOME"] = _profile_home
|
||||||
@@ -186,7 +186,7 @@ _SANE_PATH = (
|
|||||||
def _make_run_env(env: dict) -> dict:
|
def _make_run_env(env: dict) -> dict:
|
||||||
"""Build a run environment with a sane PATH and provider-var stripping."""
|
"""Build a run environment with a sane PATH and provider-var stripping."""
|
||||||
try:
|
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:
|
except Exception:
|
||||||
_is_passthrough = lambda _: False # noqa: E731
|
_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,
|
# Per-profile HOME isolation: redirect system tool configs (git, ssh, gh,
|
||||||
# npm …) into {HERMES_HOME}/home/ when that directory exists. Only the
|
# npm …) into {HERMES_HOME}/home/ when that directory exists. Only the
|
||||||
# subprocess sees the override — the Python process keeps the real HOME.
|
# 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()
|
_profile_home = get_subprocess_home()
|
||||||
if _profile_home:
|
if _profile_home:
|
||||||
run_env["HOME"] = _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.
|
execution never breaks because the config file is unreadable.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
|
|
||||||
cfg = load_config() or {}
|
cfg = load_config() or {}
|
||||||
terminal_cfg = cfg.get("terminal") or {}
|
terminal_cfg = cfg.get("terminal") or {}
|
||||||
@@ -10,12 +10,12 @@ import uuid
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
from tools.environments.modal_utils import (
|
from hermes_agent.backends.modal_utils import (
|
||||||
BaseModalExecutionEnvironment,
|
BaseModalExecutionEnvironment,
|
||||||
ModalExecStart,
|
ModalExecStart,
|
||||||
PreparedModalExec,
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -214,7 +214,7 @@ class ManagedModalEnvironment(BaseModalExecutionEnvironment):
|
|||||||
def _guard_unsupported_credential_passthrough(self) -> None:
|
def _guard_unsupported_credential_passthrough(self) -> None:
|
||||||
"""Managed Modal does not sync or mount host credential files."""
|
"""Managed Modal does not sync or mount host credential files."""
|
||||||
try:
|
try:
|
||||||
from tools.credential_files import get_credential_file_mounts
|
from hermes_agent.tools.credential_files import get_credential_file_mounts
|
||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -14,14 +14,14 @@ import threading
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_agent.constants import get_hermes_home
|
||||||
from tools.environments.base import (
|
from hermes_agent.backends.base import (
|
||||||
BaseEnvironment,
|
BaseEnvironment,
|
||||||
_ThreadedProcessHandle,
|
_ThreadedProcessHandle,
|
||||||
_load_json_store,
|
_load_json_store,
|
||||||
_save_json_store,
|
_save_json_store,
|
||||||
)
|
)
|
||||||
from tools.environments.file_sync import (
|
from hermes_agent.backends.file_sync import (
|
||||||
FileSyncManager,
|
FileSyncManager,
|
||||||
iter_sync_files,
|
iter_sync_files,
|
||||||
quoted_mkdir_command,
|
quoted_mkdir_command,
|
||||||
@@ -187,7 +187,7 @@ class ModalEnvironment(BaseEnvironment):
|
|||||||
|
|
||||||
cred_mounts = []
|
cred_mounts = []
|
||||||
try:
|
try:
|
||||||
from tools.credential_files import (
|
from hermes_agent.tools.credential_files import (
|
||||||
get_credential_file_mounts,
|
get_credential_file_mounts,
|
||||||
iter_skills_files,
|
iter_skills_files,
|
||||||
iter_cache_files,
|
iter_cache_files,
|
||||||
@@ -20,8 +20,8 @@ from abc import abstractmethod
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from tools.environments.base import BaseEnvironment
|
from hermes_agent.backends.base import BaseEnvironment
|
||||||
from tools.interrupt import is_interrupted
|
from hermes_agent.tools.interrupt import is_interrupted
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -136,7 +136,7 @@ class BaseModalExecutionEnvironment(BaseEnvironment):
|
|||||||
|
|
||||||
# Periodic activity touch so the gateway knows we're alive
|
# Periodic activity touch so the gateway knows we're alive
|
||||||
try:
|
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")
|
touch_activity_if_due(_activity_state, "modal command running")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -14,8 +14,8 @@ import uuid
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_agent.constants import get_hermes_home
|
||||||
from tools.environments.base import (
|
from hermes_agent.backends.base import (
|
||||||
BaseEnvironment,
|
BaseEnvironment,
|
||||||
_load_json_store,
|
_load_json_store,
|
||||||
_popen_bash,
|
_popen_bash,
|
||||||
@@ -75,7 +75,7 @@ def _get_scratch_dir() -> Path:
|
|||||||
scratch_path.mkdir(parents=True, exist_ok=True)
|
scratch_path.mkdir(parents=True, exist_ok=True)
|
||||||
return scratch_path
|
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"
|
sandbox = get_sandbox_dir() / "singularity"
|
||||||
|
|
||||||
scratch = Path("/scratch")
|
scratch = Path("/scratch")
|
||||||
@@ -202,7 +202,7 @@ class SingularityEnvironment(BaseEnvironment):
|
|||||||
cmd.append("--writable-tmpfs")
|
cmd.append("--writable-tmpfs")
|
||||||
|
|
||||||
try:
|
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():
|
for mount_entry in get_credential_file_mounts():
|
||||||
cmd.extend(["--bind", f"{mount_entry['host_path']}:{mount_entry['container_path']}:ro"])
|
cmd.extend(["--bind", f"{mount_entry['host_path']}:{mount_entry['container_path']}:ro"])
|
||||||
for skills_mount in get_skills_directory_mount():
|
for skills_mount in get_skills_directory_mount():
|
||||||
@@ -9,8 +9,8 @@ import subprocess
|
|||||||
import tempfile
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from tools.environments.base import BaseEnvironment, _popen_bash
|
from hermes_agent.backends.base import BaseEnvironment, _popen_bash
|
||||||
from tools.environments.file_sync import (
|
from hermes_agent.backends.file_sync import (
|
||||||
FileSyncManager,
|
FileSyncManager,
|
||||||
iter_sync_files,
|
iter_sync_files,
|
||||||
quoted_mkdir_command,
|
quoted_mkdir_command,
|
||||||
0
hermes_agent/cli/auth/__init__.py
Normal file
0
hermes_agent/cli/auth/__init__.py
Normal file
@@ -38,8 +38,8 @@ from typing import Any, Dict, List, Optional
|
|||||||
import httpx
|
import httpx
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from hermes_cli.config import get_hermes_home, get_config_path, read_raw_config
|
from hermes_agent.cli.config import get_hermes_home, get_config_path, read_raw_config
|
||||||
from hermes_constants import OPENROUTER_BASE_URL
|
from hermes_agent.constants import OPENROUTER_BASE_URL
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -329,7 +329,7 @@ def get_anthropic_key() -> str:
|
|||||||
|
|
||||||
ANTHROPIC_API_KEY -> ANTHROPIC_TOKEN -> CLAUDE_CODE_OAUTH_TOKEN
|
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:
|
for var in PROVIDER_REGISTRY["anthropic"].api_key_env_vars:
|
||||||
value = get_env_value(var) or os.getenv(var, "")
|
value = get_env_value(var) or os.getenv(var, "")
|
||||||
@@ -406,7 +406,7 @@ def _resolve_api_key_provider_secret(
|
|||||||
if provider_id == "copilot":
|
if provider_id == "copilot":
|
||||||
# Use the dedicated copilot auth module for proper token validation
|
# Use the dedicated copilot auth module for proper token validation
|
||||||
try:
|
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()
|
token, source = resolve_copilot_token()
|
||||||
if token:
|
if token:
|
||||||
return token, source
|
return token, source
|
||||||
@@ -757,16 +757,20 @@ def _save_provider_state(auth_store: Dict[str, Any], provider_id: str, state: Di
|
|||||||
auth_store["active_provider"] = provider_id
|
auth_store["active_provider"] = provider_id
|
||||||
|
|
||||||
|
|
||||||
def read_credential_pool(provider_id: Optional[str] = None) -> Dict[str, Any]:
|
def read_credential_pool() -> Dict[str, Any]:
|
||||||
"""Return the persisted credential pool, or one provider slice."""
|
"""Return the entire persisted credential pool."""
|
||||||
auth_store = _load_auth_store()
|
auth_store = _load_auth_store()
|
||||||
pool = auth_store.get("credential_pool")
|
pool = auth_store.get("credential_pool")
|
||||||
if not isinstance(pool, dict):
|
if not isinstance(pool, dict):
|
||||||
pool = {}
|
pool = {}
|
||||||
if provider_id is None:
|
return dict(pool)
|
||||||
return dict(pool)
|
|
||||||
provider_entries = pool.get(provider_id)
|
|
||||||
return list(provider_entries) if isinstance(provider_entries, list) else []
|
def read_provider_credentials(provider_id: str) -> List[Dict[str, Any]]:
|
||||||
|
"""Return credential entries for a single provider."""
|
||||||
|
pool = read_credential_pool()
|
||||||
|
entries = pool.get(provider_id)
|
||||||
|
return list(entries) if isinstance(entries, list) else []
|
||||||
|
|
||||||
|
|
||||||
def write_credential_pool(provider_id: str, entries: List[Dict[str, Any]]) -> Path:
|
def write_credential_pool(provider_id: str, entries: List[Dict[str, Any]]) -> Path:
|
||||||
@@ -862,7 +866,7 @@ def is_provider_explicitly_configured(provider_id: str) -> bool:
|
|||||||
|
|
||||||
# 2. Check config.yaml model.provider
|
# 2. Check config.yaml model.provider
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
cfg = load_config()
|
cfg = load_config()
|
||||||
model_cfg = cfg.get("model")
|
model_cfg = cfg.get("model")
|
||||||
if isinstance(model_cfg, dict):
|
if isinstance(model_cfg, dict):
|
||||||
@@ -949,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.
|
and returns a human-readable diagnostic, or empty string if nothing found.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import validate_config_structure
|
from hermes_agent.cli.config import validate_config_structure
|
||||||
issues = validate_config_structure()
|
issues = validate_config_structure()
|
||||||
if not issues:
|
if not issues:
|
||||||
return ""
|
return ""
|
||||||
@@ -1064,7 +1068,7 @@ def resolve_provider(
|
|||||||
# AWS Bedrock — detect via boto3 credential chain (IAM roles, SSO, env vars).
|
# AWS Bedrock — detect via boto3 credential chain (IAM roles, SSO, env vars).
|
||||||
# This runs after API-key providers so explicit keys always win.
|
# This runs after API-key providers so explicit keys always win.
|
||||||
try:
|
try:
|
||||||
from agent.bedrock_adapter import has_aws_credentials
|
from hermes_agent.providers.bedrock_adapter import has_aws_credentials
|
||||||
if has_aws_credentials():
|
if has_aws_credentials():
|
||||||
return "bedrock"
|
return "bedrock"
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -1327,7 +1331,7 @@ def resolve_gemini_oauth_runtime_credentials(
|
|||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Resolve runtime OAuth creds for google-gemini-cli."""
|
"""Resolve runtime OAuth creds for google-gemini-cli."""
|
||||||
try:
|
try:
|
||||||
from agent.google_oauth import (
|
from hermes_agent.providers.google_oauth import (
|
||||||
GoogleOAuthError,
|
GoogleOAuthError,
|
||||||
_credentials_path,
|
_credentials_path,
|
||||||
get_valid_access_token,
|
get_valid_access_token,
|
||||||
@@ -1366,7 +1370,7 @@ def resolve_gemini_oauth_runtime_credentials(
|
|||||||
def get_gemini_oauth_auth_status() -> Dict[str, Any]:
|
def get_gemini_oauth_auth_status() -> Dict[str, Any]:
|
||||||
"""Return a status dict for `hermes auth list` / `hermes status`."""
|
"""Return a status dict for `hermes auth list` / `hermes status`."""
|
||||||
try:
|
try:
|
||||||
from agent.google_oauth import _credentials_path, load_credentials
|
from hermes_agent.providers.google_oauth import _credentials_path, load_credentials
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return {"logged_in": False, "error": "agent.google_oauth unavailable"}
|
return {"logged_in": False, "error": "agent.google_oauth unavailable"}
|
||||||
auth_path = _credentials_path()
|
auth_path = _credentials_path()
|
||||||
@@ -2155,7 +2159,7 @@ def persist_nous_credentials(
|
|||||||
Returns the upserted :class:`PooledCredential` entry (or ``None`` if
|
Returns the upserted :class:`PooledCredential` entry (or ``None`` if
|
||||||
seeding somehow produced no match — shouldn't happen).
|
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)
|
state = dict(creds)
|
||||||
if label and str(label).strip():
|
if label and str(label).strip():
|
||||||
@@ -2436,7 +2440,7 @@ def get_nous_auth_status() -> Dict[str, Any]:
|
|||||||
# Check credential pool first — the dashboard device-code flow saves
|
# Check credential pool first — the dashboard device-code flow saves
|
||||||
# here but may not have written to the auth store yet.
|
# here but may not have written to the auth store yet.
|
||||||
try:
|
try:
|
||||||
from agent.credential_pool import load_pool
|
from hermes_agent.providers.credential_pool import load_pool
|
||||||
pool = load_pool("nous")
|
pool = load_pool("nous")
|
||||||
if pool and pool.has_credentials():
|
if pool and pool.has_credentials():
|
||||||
entry = pool.select()
|
entry = pool.select()
|
||||||
@@ -2490,7 +2494,7 @@ def get_codex_auth_status() -> Dict[str, Any]:
|
|||||||
# Check credential pool first — this is where `hermes auth` and
|
# Check credential pool first — this is where `hermes auth` and
|
||||||
# `hermes model` store device_code tokens.
|
# `hermes model` store device_code tokens.
|
||||||
try:
|
try:
|
||||||
from agent.credential_pool import load_pool
|
from hermes_agent.providers.credential_pool import load_pool
|
||||||
pool = load_pool("openai-codex")
|
pool = load_pool("openai-codex")
|
||||||
if pool and pool.has_credentials():
|
if pool and pool.has_credentials():
|
||||||
entry = pool.select()
|
entry = pool.select()
|
||||||
@@ -2611,7 +2615,7 @@ def get_auth_status(provider_id: Optional[str] = None) -> Dict[str, Any]:
|
|||||||
# AWS SDK providers (Bedrock) — check via boto3 credential chain
|
# AWS SDK providers (Bedrock) — check via boto3 credential chain
|
||||||
if pconfig and pconfig.auth_type == "aws_sdk":
|
if pconfig and pconfig.auth_type == "aws_sdk":
|
||||||
try:
|
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}
|
return {"logged_in": has_aws_credentials(), "provider": target}
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return {"logged_in": False, "provider": target, "error": "boto3 not installed"}
|
return {"logged_in": False, "provider": target, "error": "boto3 not installed"}
|
||||||
@@ -2800,7 +2804,7 @@ def _prompt_model_selection(
|
|||||||
If *unavailable_models* is provided, those models are shown grayed out
|
If *unavailable_models* is provided, those models are shown grayed out
|
||||||
and unselectable, with an upgrade link to *portal_url*.
|
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 []
|
_unavailable = unavailable_models or []
|
||||||
|
|
||||||
@@ -2910,7 +2914,7 @@ def _prompt_model_selection(
|
|||||||
title=effective_title,
|
title=effective_title,
|
||||||
)
|
)
|
||||||
idx = menu.show()
|
idx = menu.show()
|
||||||
from hermes_cli.curses_ui import flush_stdin
|
from hermes_agent.cli.ui.curses import flush_stdin
|
||||||
flush_stdin()
|
flush_stdin()
|
||||||
if idx is None:
|
if idx is None:
|
||||||
return None
|
return None
|
||||||
@@ -2967,7 +2971,7 @@ def _save_model_choice(model_id: str) -> None:
|
|||||||
The model is stored in config.yaml only — NOT in .env. This avoids
|
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.
|
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()
|
config = load_config()
|
||||||
# Always use dict format so provider/base_url can be stored alongside
|
# Always use dict format so provider/base_url can be stored alongside
|
||||||
@@ -3046,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))
|
config_path = _update_config_for_provider("openai-codex", creds.get("base_url", DEFAULT_CODEX_BASE_URL))
|
||||||
print()
|
print()
|
||||||
print("Login successful!")
|
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" Auth state: {_dhh()}/auth.json")
|
||||||
print(f" Config updated: {config_path} (model.provider=openai-codex)")
|
print(f" Config updated: {config_path} (model.provider=openai-codex)")
|
||||||
|
|
||||||
@@ -3383,7 +3387,7 @@ def _login_nous(args, pconfig: ProviderConfig) -> None:
|
|||||||
code="invalid_token",
|
code="invalid_token",
|
||||||
)
|
)
|
||||||
|
|
||||||
from hermes_cli.models import (
|
from hermes_agent.cli.models.models import (
|
||||||
_PROVIDER_MODELS, get_pricing_for_provider,
|
_PROVIDER_MODELS, get_pricing_for_provider,
|
||||||
check_nous_free_tier, partition_nous_models_by_tier,
|
check_nous_free_tier, partition_nous_models_by_tier,
|
||||||
)
|
)
|
||||||
@@ -9,7 +9,7 @@ import time
|
|||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from agent.credential_pool import (
|
from hermes_agent.providers.credential_pool import (
|
||||||
AUTH_TYPE_API_KEY,
|
AUTH_TYPE_API_KEY,
|
||||||
AUTH_TYPE_OAUTH,
|
AUTH_TYPE_OAUTH,
|
||||||
CUSTOM_POOL_PREFIX,
|
CUSTOM_POOL_PREFIX,
|
||||||
@@ -27,9 +27,9 @@ from agent.credential_pool import (
|
|||||||
list_custom_pool_providers,
|
list_custom_pool_providers,
|
||||||
load_pool,
|
load_pool,
|
||||||
)
|
)
|
||||||
import hermes_cli.auth as auth_mod
|
import hermes_agent.cli.auth.auth as auth_mod
|
||||||
from hermes_cli.auth import PROVIDER_REGISTRY
|
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
|
||||||
from hermes_constants import OPENROUTER_BASE_URL
|
from hermes_agent.constants import OPENROUTER_BASE_URL
|
||||||
|
|
||||||
|
|
||||||
# Providers that support OAuth login in addition to API keys.
|
# 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:
|
def _get_custom_provider_names() -> list:
|
||||||
"""Return list of (display_name, pool_key, provider_key) tuples."""
|
"""Return list of (display_name, pool_key, provider_key) tuples."""
|
||||||
try:
|
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()
|
config = load_config()
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -88,7 +88,7 @@ def _provider_base_url(provider: str) -> str:
|
|||||||
if provider == "openrouter":
|
if provider == "openrouter":
|
||||||
return OPENROUTER_BASE_URL
|
return OPENROUTER_BASE_URL
|
||||||
if provider.startswith(CUSTOM_POOL_PREFIX):
|
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)
|
cp_config = _get_custom_provider_config(provider)
|
||||||
if cp_config:
|
if cp_config:
|
||||||
@@ -159,7 +159,7 @@ def auth_add_command(args) -> None:
|
|||||||
# Matches the Codex device_code re-link pattern that predates this.
|
# Matches the Codex device_code re-link pattern that predates this.
|
||||||
if not provider.startswith(CUSTOM_POOL_PREFIX):
|
if not provider.startswith(CUSTOM_POOL_PREFIX):
|
||||||
try:
|
try:
|
||||||
from hermes_cli.auth import (
|
from hermes_agent.cli.auth.auth import (
|
||||||
_load_auth_store,
|
_load_auth_store,
|
||||||
unsuppress_credential_source,
|
unsuppress_credential_source,
|
||||||
)
|
)
|
||||||
@@ -197,7 +197,7 @@ def auth_add_command(args) -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if provider == "anthropic":
|
if provider == "anthropic":
|
||||||
from agent import anthropic_adapter as anthropic_mod
|
from hermes_agent.providers import anthropic_adapter as anthropic_mod
|
||||||
|
|
||||||
creds = anthropic_mod.run_hermes_oauth_login_pure()
|
creds = anthropic_mod.run_hermes_oauth_login_pure()
|
||||||
if not creds:
|
if not creds:
|
||||||
@@ -271,7 +271,7 @@ def auth_add_command(args) -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if provider == "google-gemini-cli":
|
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()
|
creds = run_gemini_oauth_login_pure()
|
||||||
label = (getattr(args, "label", None) or "").strip() or (
|
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 +
|
# handles its source-specific cleanup and we centralise suppression +
|
||||||
# user-facing output here so every source behaves identically from
|
# user-facing output here so every source behaves identically from
|
||||||
# the user's perspective.
|
# the user's perspective.
|
||||||
from agent.credential_sources import find_removal_step
|
from hermes_agent.providers.credential_sources import find_removal_step
|
||||||
from hermes_cli.auth import suppress_credential_source
|
from hermes_agent.cli.auth.auth import suppress_credential_source
|
||||||
|
|
||||||
step = find_removal_step(provider, removed.source)
|
step = find_removal_step(provider, removed.source)
|
||||||
if step is None:
|
if step is None:
|
||||||
@@ -396,7 +396,7 @@ def _interactive_auth() -> None:
|
|||||||
|
|
||||||
# Show AWS Bedrock credential status (not in the pool — uses boto3 chain)
|
# Show AWS Bedrock credential status (not in the pool — uses boto3 chain)
|
||||||
try:
|
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():
|
if has_aws_credentials():
|
||||||
auth_source = resolve_aws_auth_env_var() or "unknown"
|
auth_source = resolve_aws_auth_env_var() or "unknown"
|
||||||
region = resolve_bedrock_region()
|
region = resolve_bedrock_region()
|
||||||
@@ -558,7 +558,7 @@ def _interactive_strategy() -> None:
|
|||||||
print("Invalid choice.")
|
print("Invalid choice.")
|
||||||
return
|
return
|
||||||
|
|
||||||
from hermes_cli.config import load_config, save_config
|
from hermes_agent.cli.config import load_config, save_config
|
||||||
cfg = load_config()
|
cfg = load_config()
|
||||||
pool_strategies = cfg.get("credential_pool_strategies") or {}
|
pool_strategies = cfg.get("credential_pool_strategies") or {}
|
||||||
if not isinstance(pool_strategies, dict):
|
if not isinstance(pool_strategies, dict):
|
||||||
@@ -18,7 +18,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional, Tuple
|
from typing import Any, Callable, Optional, Tuple
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ def wait_for_registration_success(
|
|||||||
device_code: str,
|
device_code: str,
|
||||||
interval: int = 3,
|
interval: int = 3,
|
||||||
expires_in: int = 7200,
|
expires_in: int = 7200,
|
||||||
on_waiting: Optional[callable] = None,
|
on_waiting: Optional[Callable[..., Any]] = None,
|
||||||
) -> Tuple[str, str]:
|
) -> Tuple[str, str]:
|
||||||
"""Block until the registration succeeds or times out.
|
"""Block until the registration succeeds or times out.
|
||||||
|
|
||||||
@@ -234,7 +234,7 @@ def dingtalk_qr_auth() -> Optional[Tuple[str, str]]:
|
|||||||
Returns (client_id, client_secret) on success, or None if the user
|
Returns (client_id, client_secret) on success, or None if the user
|
||||||
cancelled or the flow failed.
|
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()
|
||||||
print_info(" Initializing DingTalk device authorization...")
|
print_info(" Initializing DingTalk device authorization...")
|
||||||
@@ -21,7 +21,7 @@ from datetime import datetime, timezone
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -396,7 +396,7 @@ def run_import(args) -> None:
|
|||||||
restored_profiles = []
|
restored_profiles = []
|
||||||
if profiles_dir.is_dir():
|
if profiles_dir.is_dir():
|
||||||
try:
|
try:
|
||||||
from hermes_cli.profiles import (
|
from hermes_agent.cli.profiles import (
|
||||||
create_wrapper_script, check_alias_collision,
|
create_wrapper_script, check_alias_collision,
|
||||||
_is_wrapper_dir_in_path, _get_wrapper_dir,
|
_is_wrapper_dir_in_path, _get_wrapper_dir,
|
||||||
)
|
)
|
||||||
@@ -16,9 +16,9 @@ import sys
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from hermes_cli.config import get_hermes_home, get_config_path, load_config, save_config
|
from hermes_agent.cli.config import get_hermes_home, get_config_path, load_config, save_config
|
||||||
from hermes_constants import get_optional_skills_dir
|
from hermes_agent.constants import get_optional_skills_dir
|
||||||
from hermes_cli.setup import (
|
from hermes_agent.cli.setup_wizard import (
|
||||||
Colors,
|
Colors,
|
||||||
color,
|
color,
|
||||||
print_header,
|
print_header,
|
||||||
@@ -30,7 +30,7 @@ from hermes_cli.setup import (
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
PROJECT_ROOT = Path(__file__).parent.parent.resolve()
|
PROJECT_ROOT = Path(__file__).resolve().parents[2].resolve()
|
||||||
|
|
||||||
_OPENCLAW_SCRIPT = (
|
_OPENCLAW_SCRIPT = (
|
||||||
get_optional_skills_dir(PROJECT_ROOT / "optional-skills")
|
get_optional_skills_dir(PROJECT_ROOT / "optional-skills")
|
||||||
@@ -153,7 +153,7 @@ def _warn_if_gateway_running(auto_yes: bool) -> None:
|
|||||||
(e.g. Telegram 409 "terminated by other getUpdates request"). Warn the
|
(e.g. Telegram 409 "terminated by other getUpdates request"). Warn the
|
||||||
user and let them decide whether to continue.
|
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():
|
if not get_running_pid():
|
||||||
return
|
return
|
||||||
@@ -19,7 +19,7 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -276,7 +276,7 @@ def _get_ps_exe() -> str | None:
|
|||||||
global _ps_exe
|
global _ps_exe
|
||||||
if _ps_exe is False:
|
if _ps_exe is False:
|
||||||
_ps_exe = _find_powershell()
|
_ps_exe = _find_powershell()
|
||||||
return _ps_exe
|
return _ps_exe if isinstance(_ps_exe, str) else None
|
||||||
|
|
||||||
|
|
||||||
def _windows_has_image() -> bool:
|
def _windows_has_image() -> bool:
|
||||||
@@ -395,14 +395,17 @@ def _wayland_save(dest: Path) -> bool:
|
|||||||
|
|
||||||
def _convert_to_png(path: Path) -> bool:
|
def _convert_to_png(path: Path) -> bool:
|
||||||
"""Convert an image file to PNG in-place (requires Pillow or ImageMagick)."""
|
"""Convert an image file to PNG in-place (requires Pillow or ImageMagick)."""
|
||||||
# Try Pillow first (likely installed in the venv)
|
|
||||||
try:
|
try:
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
except ImportError:
|
||||||
|
raise ImportError(
|
||||||
|
"Pillow is required for clipboard image conversion. "
|
||||||
|
"Install with: pip install hermes-agent[cli]"
|
||||||
|
) from None
|
||||||
|
try:
|
||||||
img = Image.open(path)
|
img = Image.open(path)
|
||||||
img.save(path, "PNG")
|
img.save(path, "PNG")
|
||||||
return True
|
return True
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug("Pillow BMP→PNG conversion failed: %s", e)
|
logger.debug("Pillow BMP→PNG conversion failed: %s", e)
|
||||||
|
|
||||||
@@ -318,7 +318,7 @@ def _resolve_config_gates() -> set[str]:
|
|||||||
if not gated:
|
if not gated:
|
||||||
return set()
|
return set()
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import read_raw_config
|
from hermes_agent.cli.config import read_raw_config
|
||||||
cfg = read_raw_config()
|
cfg = read_raw_config()
|
||||||
except Exception:
|
except Exception:
|
||||||
return set()
|
return set()
|
||||||
@@ -497,7 +497,7 @@ def _collect_gateway_skill_entries(
|
|||||||
# --- Tier 1: Plugin slash commands (never trimmed) ---------------------
|
# --- Tier 1: Plugin slash commands (never trimmed) ---------------------
|
||||||
plugin_pairs: list[tuple[str, str]] = []
|
plugin_pairs: list[tuple[str, str]] = []
|
||||||
try:
|
try:
|
||||||
from hermes_cli.plugins import get_plugin_commands
|
from hermes_agent.cli.plugins import get_plugin_commands
|
||||||
plugin_cmds = get_plugin_commands()
|
plugin_cmds = get_plugin_commands()
|
||||||
for cmd_name in sorted(plugin_cmds):
|
for cmd_name in sorted(plugin_cmds):
|
||||||
name = sanitize_name(cmd_name) if sanitize_name else cmd_name
|
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) -----------------
|
# --- Tier 2: Built-in skill commands (trimmed at cap) -----------------
|
||||||
_platform_disabled: set[str] = set()
|
_platform_disabled: set[str] = set()
|
||||||
try:
|
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)
|
_platform_disabled = get_disabled_skill_names(platform=platform)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
skill_triples: list[tuple[str, str, str]] = []
|
skill_triples: list[tuple[str, str, str]] = []
|
||||||
try:
|
try:
|
||||||
from agent.skill_commands import get_skill_commands
|
from hermes_agent.agent.skill_commands import get_skill_commands
|
||||||
from tools.skills_tool import SKILLS_DIR
|
from hermes_agent.tools.skills.tool import SKILLS_DIR
|
||||||
_skills_dir = str(SKILLS_DIR.resolve())
|
_skills_dir = str(SKILLS_DIR.resolve())
|
||||||
_hub_dir = str((SKILLS_DIR / ".hub").resolve())
|
_hub_dir = str((SKILLS_DIR / ".hub").resolve())
|
||||||
skill_cmds = get_skill_commands()
|
skill_cmds = get_skill_commands()
|
||||||
@@ -661,7 +661,7 @@ def discord_skill_commands_by_category(
|
|||||||
|
|
||||||
_platform_disabled: set[str] = set()
|
_platform_disabled: set[str] = set()
|
||||||
try:
|
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")
|
_platform_disabled = get_disabled_skill_names(platform="discord")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -673,8 +673,8 @@ def discord_skill_commands_by_category(
|
|||||||
hidden = 0
|
hidden = 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from agent.skill_commands import get_skill_commands
|
from hermes_agent.agent.skill_commands import get_skill_commands
|
||||||
from tools.skills_tool import SKILLS_DIR
|
from hermes_agent.tools.skills.tool import SKILLS_DIR
|
||||||
_skills_dir = SKILLS_DIR.resolve()
|
_skills_dir = SKILLS_DIR.resolve()
|
||||||
_hub_dir = (SKILLS_DIR / ".hub").resolve()
|
_hub_dir = (SKILLS_DIR / ".hub").resolve()
|
||||||
skill_cmds = get_skill_commands()
|
skill_cmds = get_skill_commands()
|
||||||
@@ -1116,7 +1116,7 @@ class SlashCommandCompleter(Completer):
|
|||||||
def _skin_completions(sub_text: str, sub_lower: str):
|
def _skin_completions(sub_text: str, sub_lower: str):
|
||||||
"""Yield completions for /skin from available skins."""
|
"""Yield completions for /skin from available skins."""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.skin_engine import list_skins
|
from hermes_agent.cli.ui.skin_engine import list_skins
|
||||||
for s in list_skins():
|
for s in list_skins():
|
||||||
name = s["name"]
|
name = s["name"]
|
||||||
if name.startswith(sub_lower) and name != sub_lower:
|
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):
|
def _personality_completions(sub_text: str, sub_lower: str):
|
||||||
"""Yield completions for /personality from configured personalities."""
|
"""Yield completions for /personality from configured personalities."""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
personalities = load_config().get("agent", {}).get("personalities", {})
|
personalities = load_config().get("agent", {}).get("personalities", {})
|
||||||
if "none".startswith(sub_lower) and "none" != sub_lower:
|
if "none".startswith(sub_lower) and "none" != sub_lower:
|
||||||
yield Completion(
|
yield Completion(
|
||||||
@@ -1162,7 +1162,7 @@ class SlashCommandCompleter(Completer):
|
|||||||
seen = set()
|
seen = set()
|
||||||
# Config-based direct aliases (preferred — include provider info)
|
# Config-based direct aliases (preferred — include provider info)
|
||||||
try:
|
try:
|
||||||
from hermes_cli.model_switch import (
|
from hermes_agent.cli.models.switch import (
|
||||||
_ensure_direct_aliases, DIRECT_ALIASES, MODEL_ALIASES,
|
_ensure_direct_aliases, DIRECT_ALIASES, MODEL_ALIASES,
|
||||||
)
|
)
|
||||||
_ensure_direct_aliases()
|
_ensure_direct_aliases()
|
||||||
@@ -1262,7 +1262,7 @@ class SlashCommandCompleter(Completer):
|
|||||||
|
|
||||||
# Plugin-registered slash commands
|
# Plugin-registered slash commands
|
||||||
try:
|
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():
|
for cmd_name, cmd_info in get_plugin_commands().items():
|
||||||
if cmd_name.startswith(word):
|
if cmd_name.startswith(word):
|
||||||
desc = str(cmd_info.get("description", "Plugin command"))
|
desc = str(cmd_info.get("description", "Plugin command"))
|
||||||
@@ -23,7 +23,7 @@ import sys
|
|||||||
import tempfile
|
import tempfile
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, Optional, List, Tuple
|
from typing import Dict, Any, Optional, List, Tuple, TypedDict, Union
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -61,8 +61,8 @@ _EXTRA_ENV_KEYS = frozenset({
|
|||||||
})
|
})
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from hermes_cli.colors import Colors, color
|
from hermes_agent.cli.ui.colors import Colors, color
|
||||||
from hermes_cli.default_soul import DEFAULT_SOUL_MD
|
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":
|
if os.environ.get("HERMES_DEV") == "1":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
from hermes_constants import is_container
|
from hermes_agent.constants import is_container
|
||||||
if is_container():
|
if is_container():
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -205,7 +205,7 @@ def get_container_exec_info() -> Optional[dict]:
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
# Re-export from hermes_constants — canonical definition lives there.
|
# 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:
|
def get_config_path() -> Path:
|
||||||
"""Get the main config file path."""
|
"""Get the main config file path."""
|
||||||
@@ -217,7 +217,7 @@ def get_env_path() -> Path:
|
|||||||
|
|
||||||
def get_project_root() -> Path:
|
def get_project_root() -> Path:
|
||||||
"""Get the project installation directory."""
|
"""Get the project installation directory."""
|
||||||
return Path(__file__).parent.parent.resolve()
|
return Path(__file__).resolve().parents[2].resolve()
|
||||||
|
|
||||||
def _secure_dir(path):
|
def _secure_dir(path):
|
||||||
"""Set directory to owner-only access (0700 by default). No-op on Windows.
|
"""Set directory to owner-only access (0700 by default). No-op on Windows.
|
||||||
@@ -343,12 +343,363 @@ def _ensure_hermes_home_managed(home: Path):
|
|||||||
# Config loading/saving
|
# Config loading/saving
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
DEFAULT_CONFIG = {
|
class _AgentConfig(TypedDict):
|
||||||
|
max_turns: int
|
||||||
|
gateway_timeout: int
|
||||||
|
restart_drain_timeout: int
|
||||||
|
service_tier: str
|
||||||
|
tool_use_enforcement: str
|
||||||
|
gateway_timeout_warning: int
|
||||||
|
gateway_notify_interval: int
|
||||||
|
|
||||||
|
class _TerminalConfig(TypedDict):
|
||||||
|
backend: str
|
||||||
|
modal_mode: str
|
||||||
|
cwd: str
|
||||||
|
timeout: int
|
||||||
|
env_passthrough: List[str]
|
||||||
|
docker_image: str
|
||||||
|
docker_forward_env: List[str]
|
||||||
|
docker_env: Dict[str, str]
|
||||||
|
singularity_image: str
|
||||||
|
modal_image: str
|
||||||
|
daytona_image: str
|
||||||
|
container_cpu: int
|
||||||
|
container_memory: int
|
||||||
|
container_disk: int
|
||||||
|
container_persistent: bool
|
||||||
|
docker_volumes: List[str]
|
||||||
|
docker_mount_cwd_to_workspace: bool
|
||||||
|
persistent_shell: bool
|
||||||
|
|
||||||
|
|
||||||
|
class _CamofoxConfig(TypedDict, total=False):
|
||||||
|
managed_persistence: bool
|
||||||
|
|
||||||
|
|
||||||
|
class _BrowserConfig(TypedDict):
|
||||||
|
inactivity_timeout: int
|
||||||
|
command_timeout: int
|
||||||
|
record_sessions: bool
|
||||||
|
allow_private_urls: bool
|
||||||
|
cdp_url: str
|
||||||
|
camofox: _CamofoxConfig
|
||||||
|
|
||||||
|
|
||||||
|
class _CheckpointsConfig(TypedDict):
|
||||||
|
enabled: bool
|
||||||
|
max_snapshots: int
|
||||||
|
|
||||||
|
|
||||||
|
class _CompressionConfig(TypedDict):
|
||||||
|
enabled: bool
|
||||||
|
threshold: float
|
||||||
|
target_ratio: float
|
||||||
|
protect_last_n: int
|
||||||
|
|
||||||
|
|
||||||
|
class _BedrockDiscoveryConfig(TypedDict):
|
||||||
|
enabled: bool
|
||||||
|
provider_filter: List[str]
|
||||||
|
refresh_interval: int
|
||||||
|
|
||||||
|
|
||||||
|
class _BedrockGuardrailConfig(TypedDict):
|
||||||
|
guardrail_identifier: str
|
||||||
|
guardrail_version: str
|
||||||
|
stream_processing_mode: str
|
||||||
|
trace: str
|
||||||
|
|
||||||
|
|
||||||
|
class _BedrockConfig(TypedDict):
|
||||||
|
region: str
|
||||||
|
discovery: _BedrockDiscoveryConfig
|
||||||
|
guardrail: _BedrockGuardrailConfig
|
||||||
|
|
||||||
|
|
||||||
|
class _AuxiliaryTaskConfig(TypedDict, total=False):
|
||||||
|
provider: str
|
||||||
|
model: str
|
||||||
|
base_url: str
|
||||||
|
api_key: str
|
||||||
|
timeout: int
|
||||||
|
extra_body: Dict[str, Any]
|
||||||
|
max_concurrency: int
|
||||||
|
download_timeout: int
|
||||||
|
|
||||||
|
|
||||||
|
class _AuxiliaryConfig(TypedDict):
|
||||||
|
vision: _AuxiliaryTaskConfig
|
||||||
|
web_extract: _AuxiliaryTaskConfig
|
||||||
|
compression: _AuxiliaryTaskConfig
|
||||||
|
session_search: _AuxiliaryTaskConfig
|
||||||
|
skills_hub: _AuxiliaryTaskConfig
|
||||||
|
approval: _AuxiliaryTaskConfig
|
||||||
|
mcp: _AuxiliaryTaskConfig
|
||||||
|
flush_memories: _AuxiliaryTaskConfig
|
||||||
|
title_generation: _AuxiliaryTaskConfig
|
||||||
|
|
||||||
|
|
||||||
|
class _UserMessagePreviewConfig(TypedDict):
|
||||||
|
first_lines: int
|
||||||
|
last_lines: int
|
||||||
|
|
||||||
|
|
||||||
|
class _DisplayConfig(TypedDict):
|
||||||
|
compact: bool
|
||||||
|
personality: str
|
||||||
|
resume_display: str
|
||||||
|
busy_input_mode: str
|
||||||
|
bell_on_complete: bool
|
||||||
|
show_reasoning: bool
|
||||||
|
streaming: bool
|
||||||
|
final_response_markdown: str
|
||||||
|
inline_diffs: bool
|
||||||
|
show_cost: bool
|
||||||
|
skin: str
|
||||||
|
user_message_preview: _UserMessagePreviewConfig
|
||||||
|
interim_assistant_messages: bool
|
||||||
|
tool_progress_command: bool
|
||||||
|
tool_progress_overrides: Dict[str, Any]
|
||||||
|
tool_preview_length: int
|
||||||
|
platforms: Dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
|
class _DashboardConfig(TypedDict):
|
||||||
|
theme: str
|
||||||
|
|
||||||
|
|
||||||
|
class _PrivacyConfig(TypedDict):
|
||||||
|
redact_pii: bool
|
||||||
|
|
||||||
|
|
||||||
|
class _EdgeTtsConfig(TypedDict):
|
||||||
|
voice: str
|
||||||
|
|
||||||
|
|
||||||
|
class _ElevenlabsTtsConfig(TypedDict):
|
||||||
|
voice_id: str
|
||||||
|
model_id: str
|
||||||
|
|
||||||
|
|
||||||
|
class _OpenaiTtsConfig(TypedDict):
|
||||||
|
model: str
|
||||||
|
voice: str
|
||||||
|
|
||||||
|
|
||||||
|
class _XaiTtsConfig(TypedDict):
|
||||||
|
voice_id: str
|
||||||
|
language: str
|
||||||
|
sample_rate: int
|
||||||
|
bit_rate: int
|
||||||
|
|
||||||
|
|
||||||
|
class _MistralTtsConfig(TypedDict):
|
||||||
|
model: str
|
||||||
|
voice_id: str
|
||||||
|
|
||||||
|
|
||||||
|
class _NeuttsConfig(TypedDict):
|
||||||
|
ref_audio: str
|
||||||
|
ref_text: str
|
||||||
|
model: str
|
||||||
|
device: str
|
||||||
|
|
||||||
|
|
||||||
|
class _TtsConfig(TypedDict):
|
||||||
|
provider: str
|
||||||
|
edge: _EdgeTtsConfig
|
||||||
|
elevenlabs: _ElevenlabsTtsConfig
|
||||||
|
openai: _OpenaiTtsConfig
|
||||||
|
xai: _XaiTtsConfig
|
||||||
|
mistral: _MistralTtsConfig
|
||||||
|
neutts: _NeuttsConfig
|
||||||
|
|
||||||
|
|
||||||
|
class _LocalSttConfig(TypedDict):
|
||||||
|
model: str
|
||||||
|
language: str
|
||||||
|
|
||||||
|
|
||||||
|
class _OpenaiSttConfig(TypedDict):
|
||||||
|
model: str
|
||||||
|
|
||||||
|
|
||||||
|
class _MistralSttConfig(TypedDict):
|
||||||
|
model: str
|
||||||
|
|
||||||
|
|
||||||
|
class _SttConfig(TypedDict):
|
||||||
|
enabled: bool
|
||||||
|
provider: str
|
||||||
|
local: _LocalSttConfig
|
||||||
|
openai: _OpenaiSttConfig
|
||||||
|
mistral: _MistralSttConfig
|
||||||
|
|
||||||
|
|
||||||
|
class _VoiceConfig(TypedDict):
|
||||||
|
record_key: str
|
||||||
|
max_recording_seconds: int
|
||||||
|
auto_tts: bool
|
||||||
|
silence_threshold: int
|
||||||
|
silence_duration: float
|
||||||
|
|
||||||
|
|
||||||
|
class _HumanDelayConfig(TypedDict):
|
||||||
|
mode: str
|
||||||
|
min_ms: int
|
||||||
|
max_ms: int
|
||||||
|
|
||||||
|
|
||||||
|
class _ContextConfig(TypedDict):
|
||||||
|
engine: str
|
||||||
|
|
||||||
|
|
||||||
|
class _MemoryConfig(TypedDict):
|
||||||
|
memory_enabled: bool
|
||||||
|
user_profile_enabled: bool
|
||||||
|
memory_char_limit: int
|
||||||
|
user_char_limit: int
|
||||||
|
provider: str
|
||||||
|
|
||||||
|
|
||||||
|
class _DelegationConfig(TypedDict):
|
||||||
|
model: str
|
||||||
|
provider: str
|
||||||
|
base_url: str
|
||||||
|
api_key: str
|
||||||
|
max_iterations: int
|
||||||
|
reasoning_effort: str
|
||||||
|
|
||||||
|
|
||||||
|
class _SkillsConfig(TypedDict):
|
||||||
|
external_dirs: List[str]
|
||||||
|
|
||||||
|
|
||||||
|
class _ChannelPromptsConfig(TypedDict):
|
||||||
|
channel_prompts: Dict[str, str]
|
||||||
|
|
||||||
|
|
||||||
|
class _DiscordConfig(TypedDict):
|
||||||
|
require_mention: bool
|
||||||
|
free_response_channels: str
|
||||||
|
allowed_channels: str
|
||||||
|
auto_thread: bool
|
||||||
|
reactions: bool
|
||||||
|
channel_prompts: Dict[str, str]
|
||||||
|
server_actions: str
|
||||||
|
|
||||||
|
|
||||||
|
class _ApprovalsConfig(TypedDict):
|
||||||
|
mode: str
|
||||||
|
timeout: int
|
||||||
|
cron_mode: str
|
||||||
|
|
||||||
|
|
||||||
|
class _WebsiteBlocklistConfig(TypedDict):
|
||||||
|
enabled: bool
|
||||||
|
domains: List[str]
|
||||||
|
shared_files: List[str]
|
||||||
|
|
||||||
|
|
||||||
|
class _SecurityConfig(TypedDict):
|
||||||
|
redact_secrets: bool
|
||||||
|
tirith_enabled: bool
|
||||||
|
tirith_path: str
|
||||||
|
tirith_timeout: int
|
||||||
|
tirith_fail_open: bool
|
||||||
|
website_blocklist: _WebsiteBlocklistConfig
|
||||||
|
|
||||||
|
|
||||||
|
class _CronConfig(TypedDict):
|
||||||
|
wrap_response: bool
|
||||||
|
max_parallel_jobs: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
|
class _CodeExecutionConfig(TypedDict):
|
||||||
|
mode: str
|
||||||
|
|
||||||
|
|
||||||
|
class _LoggingConfig(TypedDict):
|
||||||
|
level: str
|
||||||
|
max_size_mb: int
|
||||||
|
backup_count: int
|
||||||
|
|
||||||
|
|
||||||
|
class _NetworkConfig(TypedDict):
|
||||||
|
force_ipv4: bool
|
||||||
|
|
||||||
|
|
||||||
|
class _DefaultConfig(TypedDict):
|
||||||
|
model: str
|
||||||
|
providers: Dict[str, Any]
|
||||||
|
fallback_providers: List[Any]
|
||||||
|
credential_pool_strategies: Dict[str, Any]
|
||||||
|
toolsets: List[str]
|
||||||
|
agent: _AgentConfig
|
||||||
|
terminal: _TerminalConfig
|
||||||
|
browser: _BrowserConfig
|
||||||
|
checkpoints: _CheckpointsConfig
|
||||||
|
file_read_max_chars: int
|
||||||
|
compression: _CompressionConfig
|
||||||
|
bedrock: _BedrockConfig
|
||||||
|
auxiliary: _AuxiliaryConfig
|
||||||
|
display: _DisplayConfig
|
||||||
|
dashboard: _DashboardConfig
|
||||||
|
privacy: _PrivacyConfig
|
||||||
|
tts: _TtsConfig
|
||||||
|
stt: _SttConfig
|
||||||
|
voice: _VoiceConfig
|
||||||
|
human_delay: _HumanDelayConfig
|
||||||
|
context: _ContextConfig
|
||||||
|
memory: _MemoryConfig
|
||||||
|
delegation: _DelegationConfig
|
||||||
|
prefill_messages_file: str
|
||||||
|
skills: _SkillsConfig
|
||||||
|
honcho: Dict[str, Any]
|
||||||
|
timezone: str
|
||||||
|
discord: _DiscordConfig
|
||||||
|
whatsapp: Dict[str, Any]
|
||||||
|
telegram: _ChannelPromptsConfig
|
||||||
|
slack: _ChannelPromptsConfig
|
||||||
|
mattermost: _ChannelPromptsConfig
|
||||||
|
approvals: _ApprovalsConfig
|
||||||
|
command_allowlist: List[str]
|
||||||
|
quick_commands: Dict[str, Any]
|
||||||
|
hooks: Dict[str, Any]
|
||||||
|
hooks_auto_accept: bool
|
||||||
|
personalities: Dict[str, Any]
|
||||||
|
security: _SecurityConfig
|
||||||
|
cron: _CronConfig
|
||||||
|
code_execution: _CodeExecutionConfig
|
||||||
|
logging: _LoggingConfig
|
||||||
|
network: _NetworkConfig
|
||||||
|
_config_version: int
|
||||||
|
|
||||||
|
|
||||||
|
class _EnvVarRequired(TypedDict):
|
||||||
|
description: str
|
||||||
|
prompt: str
|
||||||
|
category: str
|
||||||
|
|
||||||
|
|
||||||
|
class _EnvVarOptional(TypedDict, total=False):
|
||||||
|
url: Optional[str]
|
||||||
|
password: bool
|
||||||
|
tools: List[str]
|
||||||
|
advanced: bool
|
||||||
|
|
||||||
|
|
||||||
|
class _EnvVarInfo(_EnvVarRequired, _EnvVarOptional):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_CONFIG: _DefaultConfig = {
|
||||||
"model": "",
|
"model": "",
|
||||||
"providers": {},
|
"providers": {},
|
||||||
"fallback_providers": [],
|
"fallback_providers": [],
|
||||||
"credential_pool_strategies": {},
|
"credential_pool_strategies": {},
|
||||||
"toolsets": ["hermes-cli"],
|
"hermes_agent.tools.toolsets": ["hermes-cli"],
|
||||||
"agent": {
|
"agent": {
|
||||||
"max_turns": 90,
|
"max_turns": 90,
|
||||||
# Inactivity timeout for gateway agent execution (seconds).
|
# Inactivity timeout for gateway agent execution (seconds).
|
||||||
@@ -919,7 +1270,7 @@ ENV_VARS_BY_VERSION: Dict[int, List[str]] = {
|
|||||||
REQUIRED_ENV_VARS = {}
|
REQUIRED_ENV_VARS = {}
|
||||||
|
|
||||||
# Optional environment variables that enhance functionality
|
# Optional environment variables that enhance functionality
|
||||||
OPTIONAL_ENV_VARS = {
|
OPTIONAL_ENV_VARS: Dict[str, _EnvVarInfo] = {
|
||||||
# ── Provider (handled in provider selection, not shown in checklists) ──
|
# ── Provider (handled in provider selection, not shown in checklists) ──
|
||||||
"NOUS_BASE_URL": {
|
"NOUS_BASE_URL": {
|
||||||
"description": "Nous Portal base URL override",
|
"description": "Nous Portal base URL override",
|
||||||
@@ -1853,7 +2204,7 @@ def get_missing_config_fields() -> List[Dict[str, Any]]:
|
|||||||
config = load_config()
|
config = load_config()
|
||||||
missing = []
|
missing = []
|
||||||
|
|
||||||
def _check(defaults: dict, current: dict, prefix: str = ""):
|
def _check(defaults: Dict[str, Any], current: Dict[str, Any], prefix: str = ""):
|
||||||
for key, default_value in defaults.items():
|
for key, default_value in defaults.items():
|
||||||
if key.startswith('_'):
|
if key.startswith('_'):
|
||||||
continue
|
continue
|
||||||
@@ -1867,7 +2218,7 @@ def get_missing_config_fields() -> List[Dict[str, Any]]:
|
|||||||
elif isinstance(default_value, dict) and isinstance(current.get(key), dict):
|
elif isinstance(default_value, dict) and isinstance(current.get(key), dict):
|
||||||
_check(default_value, current[key], full_key)
|
_check(default_value, current[key], full_key)
|
||||||
|
|
||||||
_check(DEFAULT_CONFIG, config)
|
_check(dict(DEFAULT_CONFIG), config)
|
||||||
return missing
|
return missing
|
||||||
|
|
||||||
|
|
||||||
@@ -1879,7 +2230,7 @@ def get_missing_skill_config_vars() -> List[Dict[str, Any]]:
|
|||||||
config.yaml. Returns a list of dicts suitable for prompting.
|
config.yaml. Returns a list of dicts suitable for prompting.
|
||||||
"""
|
"""
|
||||||
try:
|
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:
|
except Exception:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -2087,8 +2438,8 @@ def check_config_version() -> Tuple[int, int]:
|
|||||||
Returns (current_version, latest_version).
|
Returns (current_version, latest_version).
|
||||||
"""
|
"""
|
||||||
config = load_config()
|
config = load_config()
|
||||||
current = config.get("_config_version", 0)
|
current = int(config.get("_config_version", 0))
|
||||||
latest = DEFAULT_CONFIG.get("_config_version", 1)
|
latest = int(DEFAULT_CONFIG.get("_config_version", 1))
|
||||||
return current, latest
|
return current, latest
|
||||||
|
|
||||||
|
|
||||||
@@ -2099,7 +2450,7 @@ def check_config_version() -> Tuple[int, int]:
|
|||||||
# Fields that are valid at root level of config.yaml
|
# Fields that are valid at root level of config.yaml
|
||||||
_KNOWN_ROOT_KEYS = {
|
_KNOWN_ROOT_KEYS = {
|
||||||
"_config_version", "model", "providers", "fallback_model",
|
"_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",
|
"agent", "terminal", "display", "compression", "delegation",
|
||||||
"auxiliary", "custom_providers", "context", "memory", "gateway",
|
"auxiliary", "custom_providers", "context", "memory", "gateway",
|
||||||
}
|
}
|
||||||
@@ -2781,7 +3132,7 @@ def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, A
|
|||||||
print()
|
print()
|
||||||
config = load_config()
|
config = load_config()
|
||||||
try:
|
try:
|
||||||
from agent.skill_utils import SKILL_CONFIG_PREFIX
|
from hermes_agent.agent.skill_utils import SKILL_CONFIG_PREFIX
|
||||||
except Exception:
|
except Exception:
|
||||||
SKILL_CONFIG_PREFIX = "skills.config"
|
SKILL_CONFIG_PREFIX = "skills.config"
|
||||||
for var in missing_skill_config:
|
for var in missing_skill_config:
|
||||||
@@ -2807,7 +3158,7 @@ def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, A
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def _deep_merge(base: dict, override: dict) -> dict:
|
def _deep_merge(base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Recursively merge *override* into *base*, preserving nested defaults.
|
"""Recursively merge *override* into *base*, preserving nested defaults.
|
||||||
|
|
||||||
Keys in *override* take precedence. If both values are dicts the merge
|
Keys in *override* take precedence. If both values are dicts the merge
|
||||||
@@ -2996,7 +3347,7 @@ def load_config() -> Dict[str, Any]:
|
|||||||
ensure_hermes_home()
|
ensure_hermes_home()
|
||||||
config_path = get_config_path()
|
config_path = get_config_path()
|
||||||
|
|
||||||
config = copy.deepcopy(DEFAULT_CONFIG)
|
config: Dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG)
|
||||||
|
|
||||||
if config_path.exists():
|
if config_path.exists():
|
||||||
try:
|
try:
|
||||||
@@ -3096,7 +3447,7 @@ def save_config(config: Dict[str, Any]):
|
|||||||
if is_managed():
|
if is_managed():
|
||||||
managed_error("save configuration")
|
managed_error("save configuration")
|
||||||
return
|
return
|
||||||
from utils import atomic_yaml_write
|
from hermes_agent.utils import atomic_yaml_write
|
||||||
|
|
||||||
ensure_hermes_home()
|
ensure_hermes_home()
|
||||||
config_path = get_config_path()
|
config_path = get_config_path()
|
||||||
@@ -3532,7 +3883,7 @@ def show_config():
|
|||||||
for env_key, name in keys:
|
for env_key, name in keys:
|
||||||
value = get_env_value(env_key)
|
value = get_env_value(env_key)
|
||||||
print(f" {name:<14} {redact_key(value)}")
|
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()
|
anthropic_value = get_anthropic_key()
|
||||||
print(f" {'Anthropic':<14} {redact_key(anthropic_value)}")
|
print(f" {'Anthropic':<14} {redact_key(anthropic_value)}")
|
||||||
|
|
||||||
@@ -3640,7 +3991,7 @@ def show_config():
|
|||||||
|
|
||||||
# Skill config
|
# Skill config
|
||||||
try:
|
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()
|
skill_vars = discover_all_skill_config_vars()
|
||||||
if skill_vars:
|
if skill_vars:
|
||||||
resolved = resolve_skill_config_values(skill_vars)
|
resolved = resolve_skill_config_values(skill_vars)
|
||||||
@@ -3672,7 +4023,7 @@ def edit_config():
|
|||||||
|
|
||||||
# Ensure config exists
|
# Ensure config exists
|
||||||
if not config_path.exists():
|
if not config_path.exists():
|
||||||
save_config(DEFAULT_CONFIG)
|
save_config(dict(DEFAULT_CONFIG))
|
||||||
print(f"Created {config_path}")
|
print(f"Created {config_path}")
|
||||||
|
|
||||||
# Find editor
|
# Find editor
|
||||||
@@ -3754,7 +4105,7 @@ def set_config_value(key: str, value: str):
|
|||||||
|
|
||||||
# Write only user config back (not the full merged defaults)
|
# Write only user config back (not the full merged defaults)
|
||||||
ensure_hermes_home()
|
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)
|
atomic_yaml_write(config_path, user_config, sort_keys=False)
|
||||||
|
|
||||||
# Keep .env in sync for keys that terminal_tool reads directly from env vars.
|
# Keep .env in sync for keys that terminal_tool reads directly from env vars.
|
||||||
@@ -10,10 +10,9 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterable, List, Optional
|
from typing import Iterable, List, Optional
|
||||||
|
|
||||||
PROJECT_ROOT = Path(__file__).parent.parent.resolve()
|
PROJECT_ROOT = Path(__file__).resolve().parents[2].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]]:
|
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):
|
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))
|
return json.loads(cronjob_tool(**kwargs))
|
||||||
|
|
||||||
|
|
||||||
def cron_list(show_all: bool = False):
|
def cron_list(show_all: bool = False):
|
||||||
"""List all scheduled jobs."""
|
"""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)
|
jobs = list_jobs(include_disabled=show_all)
|
||||||
|
|
||||||
@@ -110,7 +109,7 @@ def cron_list(show_all: bool = False):
|
|||||||
|
|
||||||
print()
|
print()
|
||||||
|
|
||||||
from hermes_cli.gateway import find_gateway_pids
|
from hermes_agent.cli.gateway import find_gateway_pids
|
||||||
if not find_gateway_pids():
|
if not find_gateway_pids():
|
||||||
print(color(" ⚠ Gateway is not running — jobs won't fire automatically.", Colors.YELLOW))
|
print(color(" ⚠ Gateway is not running — jobs won't fire automatically.", Colors.YELLOW))
|
||||||
print(color(" Start it with: hermes gateway install", Colors.DIM))
|
print(color(" Start it with: hermes gateway install", Colors.DIM))
|
||||||
@@ -120,14 +119,14 @@ def cron_list(show_all: bool = False):
|
|||||||
|
|
||||||
def cron_tick():
|
def cron_tick():
|
||||||
"""Run due jobs once and exit."""
|
"""Run due jobs once and exit."""
|
||||||
from cron.scheduler import tick
|
from hermes_agent.cron.scheduler import tick
|
||||||
tick(verbose=True)
|
tick(verbose=True)
|
||||||
|
|
||||||
|
|
||||||
def cron_status():
|
def cron_status():
|
||||||
"""Show cron execution status."""
|
"""Show cron execution status."""
|
||||||
from cron.jobs import list_jobs
|
from hermes_agent.cron.jobs import list_jobs
|
||||||
from hermes_cli.gateway import find_gateway_pids
|
from hermes_agent.cli.gateway import find_gateway_pids
|
||||||
|
|
||||||
print()
|
print()
|
||||||
|
|
||||||
@@ -185,7 +184,7 @@ def cron_create(args):
|
|||||||
|
|
||||||
|
|
||||||
def cron_edit(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)
|
job = get_job(args.job_id)
|
||||||
if not job:
|
if not job:
|
||||||
@@ -16,7 +16,7 @@ import urllib.request
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
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.
|
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)
|
filename = LOG_FILES.get(log_name)
|
||||||
if not filename:
|
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:
|
def _read_log_tail(log_name: str, num_lines: int) -> str:
|
||||||
"""Read the last *num_lines* from a log file, or return a placeholder."""
|
"""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)
|
log_path = _resolve_log_path(log_name)
|
||||||
if log_path is None:
|
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:
|
def _capture_dump() -> str:
|
||||||
"""Run ``hermes dump`` and return its stdout as a string."""
|
"""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:
|
class _FakeArgs:
|
||||||
show_keys = False
|
show_keys = False
|
||||||
@@ -10,8 +10,8 @@ import subprocess
|
|||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from hermes_cli.config import get_project_root, get_hermes_home, get_env_path
|
from hermes_agent.cli.config import get_project_root, get_hermes_home, get_env_path
|
||||||
from hermes_constants import display_hermes_home
|
from hermes_agent.constants import display_hermes_home
|
||||||
|
|
||||||
PROJECT_ROOT = get_project_root()
|
PROJECT_ROOT = get_project_root()
|
||||||
HERMES_HOME = get_hermes_home()
|
HERMES_HOME = get_hermes_home()
|
||||||
@@ -28,9 +28,9 @@ if _env_path.exists():
|
|||||||
# Also try project .env as dev fallback
|
# Also try project .env as dev fallback
|
||||||
load_dotenv(PROJECT_ROOT / ".env", override=False, encoding="utf-8")
|
load_dotenv(PROJECT_ROOT / ".env", override=False, encoding="utf-8")
|
||||||
|
|
||||||
from hermes_cli.colors import Colors, color
|
from hermes_agent.cli.ui.colors import Colors, color
|
||||||
from hermes_constants import OPENROUTER_MODELS_URL
|
from hermes_agent.constants import OPENROUTER_MODELS_URL
|
||||||
from utils import base_url_host_matches
|
from hermes_agent.utils import base_url_host_matches
|
||||||
|
|
||||||
|
|
||||||
_PROVIDER_ENV_HINTS = (
|
_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:
|
def _python_install_cmd() -> str:
|
||||||
@@ -92,7 +92,7 @@ def _has_provider_env_config(content: str) -> bool:
|
|||||||
def _honcho_is_configured_for_doctor() -> bool:
|
def _honcho_is_configured_for_doctor() -> bool:
|
||||||
"""Return True when Honcho is configured, even if this process has no active session."""
|
"""Return True when Honcho is configured, even if this process has no active session."""
|
||||||
try:
|
try:
|
||||||
from plugins.memory.honcho.client import HonchoClientConfig
|
from hermes_agent.plugins.memory.honcho.client import HonchoClientConfig
|
||||||
|
|
||||||
cfg = HonchoClientConfig.from_global_config()
|
cfg = HonchoClientConfig.from_global_config()
|
||||||
return bool(cfg.enabled and (cfg.api_key or cfg.base_url))
|
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:
|
def _check_gateway_service_linger(issues: list[str]) -> None:
|
||||||
"""Warn when a systemd user gateway service will stop after logout."""
|
"""Warn when a systemd user gateway service will stop after logout."""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.gateway import (
|
from hermes_agent.cli.gateway import (
|
||||||
get_systemd_linger_status,
|
get_systemd_linger_status,
|
||||||
get_systemd_unit_path,
|
get_systemd_unit_path,
|
||||||
is_linux,
|
is_linux,
|
||||||
@@ -290,12 +290,12 @@ def run_doctor(args):
|
|||||||
|
|
||||||
known_providers: set = set()
|
known_providers: set = set()
|
||||||
try:
|
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"}
|
known_providers = set(PROVIDER_REGISTRY.keys()) | {"openrouter", "custom", "auto"}
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
try:
|
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:
|
except Exception:
|
||||||
_resolve_provider = None
|
_resolve_provider = None
|
||||||
|
|
||||||
@@ -338,7 +338,7 @@ def run_doctor(args):
|
|||||||
# explicitly dispatch, which would produce false positives.
|
# explicitly dispatch, which would produce false positives.
|
||||||
if canonical_provider and canonical_provider not in ("auto", "custom", "openrouter"):
|
if canonical_provider and canonical_provider not in ("auto", "custom", "openrouter"):
|
||||||
try:
|
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)
|
pconfig = PROVIDER_REGISTRY.get(canonical_provider)
|
||||||
if pconfig and getattr(pconfig, "auth_type", "") == "api_key":
|
if pconfig and getattr(pconfig, "auth_type", "") == "api_key":
|
||||||
status = get_auth_status(canonical_provider) or {}
|
status = get_auth_status(canonical_provider) or {}
|
||||||
@@ -379,7 +379,7 @@ def run_doctor(args):
|
|||||||
config_path = HERMES_HOME / 'config.yaml'
|
config_path = HERMES_HOME / 'config.yaml'
|
||||||
if config_path.exists():
|
if config_path.exists():
|
||||||
try:
|
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()
|
current_ver, latest_ver = check_config_version()
|
||||||
if current_ver < latest_ver:
|
if current_ver < latest_ver:
|
||||||
check_warn(
|
check_warn(
|
||||||
@@ -419,7 +419,7 @@ def run_doctor(args):
|
|||||||
model_section[k] = raw_config.pop(k)
|
model_section[k] = raw_config.pop(k)
|
||||||
else:
|
else:
|
||||||
raw_config.pop(k)
|
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)
|
atomic_yaml_write(config_path, raw_config)
|
||||||
check_ok("Migrated stale root-level keys into model section")
|
check_ok("Migrated stale root-level keys into model section")
|
||||||
fixed_count += 1
|
fixed_count += 1
|
||||||
@@ -430,7 +430,7 @@ def run_doctor(args):
|
|||||||
|
|
||||||
# Validate config structure (catches malformed custom_providers, etc.)
|
# Validate config structure (catches malformed custom_providers, etc.)
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import validate_config_structure
|
from hermes_agent.cli.config import validate_config_structure
|
||||||
config_issues = validate_config_structure()
|
config_issues = validate_config_structure()
|
||||||
if config_issues:
|
if config_issues:
|
||||||
print()
|
print()
|
||||||
@@ -454,7 +454,7 @@ def run_doctor(args):
|
|||||||
print(color("◆ Auth Providers", Colors.CYAN, Colors.BOLD))
|
print(color("◆ Auth Providers", Colors.CYAN, Colors.BOLD))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from hermes_cli.auth import (
|
from hermes_agent.cli.auth.auth import (
|
||||||
get_nous_auth_status,
|
get_nous_auth_status,
|
||||||
get_codex_auth_status,
|
get_codex_auth_status,
|
||||||
get_gemini_oauth_auth_status,
|
get_gemini_oauth_auth_status,
|
||||||
@@ -877,13 +877,13 @@ def run_doctor(args):
|
|||||||
else:
|
else:
|
||||||
check_warn("OpenRouter API", "(not configured)")
|
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()
|
anthropic_key = get_anthropic_key()
|
||||||
if anthropic_key:
|
if anthropic_key:
|
||||||
print(" Checking Anthropic API...", end="", flush=True)
|
print(" Checking Anthropic API...", end="", flush=True)
|
||||||
try:
|
try:
|
||||||
import httpx
|
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"}
|
headers = {"anthropic-version": "2023-06-01"}
|
||||||
if _is_oauth_token(anthropic_key):
|
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
|
# with no /v1) don't support /models. Rewrite to the OpenAI-compat
|
||||||
# /v1 surface for health checks.
|
# /v1 surface for health checks.
|
||||||
if _base and _base.rstrip("/").endswith("/anthropic"):
|
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)
|
_base = _to_openai_base_url(_base)
|
||||||
if base_url_host_matches(_base, "api.kimi.com") and _base.rstrip("/").endswith("/coding"):
|
if base_url_host_matches(_base, "api.kimi.com") and _base.rstrip("/").endswith("/coding"):
|
||||||
_base = _base.rstrip("/") + "/v1"
|
_base = _base.rstrip("/") + "/v1"
|
||||||
@@ -977,7 +977,7 @@ def run_doctor(args):
|
|||||||
# -- AWS Bedrock --
|
# -- AWS Bedrock --
|
||||||
# Bedrock uses the AWS SDK credential chain, not API keys.
|
# Bedrock uses the AWS SDK credential chain, not API keys.
|
||||||
try:
|
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():
|
if has_aws_credentials():
|
||||||
_auth_var = resolve_aws_auth_env_var()
|
_auth_var = resolve_aws_auth_env_var()
|
||||||
_region = resolve_bedrock_region()
|
_region = resolve_bedrock_region()
|
||||||
@@ -1028,9 +1028,7 @@ def run_doctor(args):
|
|||||||
print(color("◆ Tool Availability", Colors.CYAN, Colors.BOLD))
|
print(color("◆ Tool Availability", Colors.CYAN, Colors.BOLD))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Add project root to path for imports
|
from hermes_agent.tools.dispatch import check_tool_availability, TOOLSET_REQUIREMENTS
|
||||||
sys.path.insert(0, str(PROJECT_ROOT))
|
|
||||||
from model_tools import check_tool_availability, TOOLSET_REQUIREMENTS
|
|
||||||
|
|
||||||
available, unavailable = check_tool_availability()
|
available, unavailable = check_tool_availability()
|
||||||
available, unavailable = _apply_doctor_tool_availability_overrides(available, unavailable)
|
available, unavailable = _apply_doctor_tool_availability_overrides(available, unavailable)
|
||||||
@@ -1079,7 +1077,7 @@ def run_doctor(args):
|
|||||||
else:
|
else:
|
||||||
check_warn("Skills Hub directory not initialized", "(run: hermes skills list)")
|
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")
|
github_token = get_env_value("GITHUB_TOKEN") or get_env_value("GH_TOKEN")
|
||||||
if github_token:
|
if github_token:
|
||||||
check_ok("GitHub token configured (authenticated API access)")
|
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)")
|
check_ok("Built-in memory active", "(no external provider configured — this is fine)")
|
||||||
elif _active_memory_provider == "honcho":
|
elif _active_memory_provider == "honcho":
|
||||||
try:
|
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()
|
hcfg = HonchoClientConfig.from_global_config()
|
||||||
_honcho_cfg_path = resolve_config_path()
|
_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")
|
check_fail("Honcho API key or base URL not set", "run: hermes memory setup")
|
||||||
issues.append("No Honcho API key — run 'hermes memory setup'")
|
issues.append("No Honcho API key — run 'hermes memory setup'")
|
||||||
else:
|
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()
|
reset_honcho_client()
|
||||||
try:
|
try:
|
||||||
get_honcho_client(hcfg)
|
get_honcho_client(hcfg)
|
||||||
@@ -1137,7 +1135,7 @@ def run_doctor(args):
|
|||||||
check_warn("Honcho check failed", str(_e))
|
check_warn("Honcho check failed", str(_e))
|
||||||
elif _active_memory_provider == "mem0":
|
elif _active_memory_provider == "mem0":
|
||||||
try:
|
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_cfg = _load_mem0_config()
|
||||||
mem0_key = mem0_cfg.get("api_key", "")
|
mem0_key = mem0_cfg.get("api_key", "")
|
||||||
if mem0_key:
|
if mem0_key:
|
||||||
@@ -1154,7 +1152,7 @@ def run_doctor(args):
|
|||||||
else:
|
else:
|
||||||
# Generic check for other memory providers (openviking, hindsight, etc.)
|
# Generic check for other memory providers (openviking, hindsight, etc.)
|
||||||
try:
|
try:
|
||||||
from plugins.memory import load_memory_provider
|
from hermes_agent.plugins.memory import load_memory_provider
|
||||||
_provider = load_memory_provider(_active_memory_provider)
|
_provider = load_memory_provider(_active_memory_provider)
|
||||||
if _provider and _provider.is_available():
|
if _provider and _provider.is_available():
|
||||||
check_ok(f"{_active_memory_provider} provider active")
|
check_ok(f"{_active_memory_provider} provider active")
|
||||||
@@ -1169,7 +1167,7 @@ def run_doctor(args):
|
|||||||
# Profiles
|
# Profiles
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
try:
|
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
|
import re as _re
|
||||||
|
|
||||||
named_profiles = [p for p in list_profiles() if not p.is_default]
|
named_profiles = [p for p in list_profiles() if not p.is_default]
|
||||||
@@ -13,8 +13,8 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from hermes_cli.config import get_hermes_home, get_env_path, get_project_root, load_config
|
from hermes_agent.cli.config import get_hermes_home, get_env_path, get_project_root, load_config
|
||||||
from hermes_constants import display_hermes_home
|
from hermes_agent.constants import display_hermes_home
|
||||||
|
|
||||||
|
|
||||||
def _get_git_commit(project_root: Path) -> str:
|
def _get_git_commit(project_root: Path) -> str:
|
||||||
@@ -44,7 +44,7 @@ def _redact(value: str) -> str:
|
|||||||
def _gateway_status() -> str:
|
def _gateway_status() -> str:
|
||||||
"""Return a short gateway status string."""
|
"""Return a short gateway status string."""
|
||||||
try:
|
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()
|
snapshot = get_gateway_runtime_snapshot()
|
||||||
if snapshot.running:
|
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.
|
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 = {}
|
overrides = {}
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ def _config_overrides(config: dict) -> dict[str, str]:
|
|||||||
default_toolsets = DEFAULT_CONFIG.get("toolsets", [])
|
default_toolsets = DEFAULT_CONFIG.get("toolsets", [])
|
||||||
user_toolsets = config.get("toolsets", [])
|
user_toolsets = config.get("toolsets", [])
|
||||||
if user_toolsets != default_toolsets:
|
if user_toolsets != default_toolsets:
|
||||||
overrides["toolsets"] = str(user_toolsets)
|
overrides["hermes_agent.tools.toolsets"] = str(user_toolsets)
|
||||||
|
|
||||||
# Fallback providers
|
# Fallback providers
|
||||||
fallbacks = config.get("fallback_providers", [])
|
fallbacks = config.get("fallback_providers", [])
|
||||||
@@ -207,7 +207,7 @@ def run_dump(args):
|
|||||||
hermes_home = get_hermes_home()
|
hermes_home = get_hermes_home()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from hermes_cli import __version__, __release_date__
|
from hermes_agent.cli import __version__, __release_date__
|
||||||
except ImportError:
|
except ImportError:
|
||||||
__version__ = "(unknown)"
|
__version__ = "(unknown)"
|
||||||
__release_date__ = ""
|
__release_date__ = ""
|
||||||
@@ -223,7 +223,7 @@ def run_dump(args):
|
|||||||
|
|
||||||
# Profile
|
# Profile
|
||||||
try:
|
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)"
|
profile = get_active_profile_name() or "(default)"
|
||||||
except Exception:
|
except Exception:
|
||||||
profile = "(default)"
|
profile = "(default)"
|
||||||
@@ -108,7 +108,7 @@ def _sanitize_env_file_if_needed(path: Path) -> None:
|
|||||||
if not path.exists():
|
if not path.exists():
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import _sanitize_env_lines
|
from hermes_agent.cli.config import _sanitize_env_lines
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return # early bootstrap — config module not available yet
|
return # early bootstrap — config module not available yet
|
||||||
|
|
||||||
@@ -13,15 +13,15 @@ import sys
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
PROJECT_ROOT = Path(__file__).parent.parent.resolve()
|
PROJECT_ROOT = Path(__file__).resolve().parents[2].resolve()
|
||||||
|
|
||||||
from gateway.status import terminate_pid
|
from hermes_agent.gateway.status import terminate_pid
|
||||||
from gateway.restart import (
|
from hermes_agent.gateway.restart import (
|
||||||
DEFAULT_GATEWAY_RESTART_DRAIN_TIMEOUT,
|
DEFAULT_GATEWAY_RESTART_DRAIN_TIMEOUT,
|
||||||
GATEWAY_SERVICE_RESTART_EXIT_CODE,
|
GATEWAY_SERVICE_RESTART_EXIT_CODE,
|
||||||
parse_restart_drain_timeout,
|
parse_restart_drain_timeout,
|
||||||
)
|
)
|
||||||
from hermes_cli.config import (
|
from hermes_agent.cli.config import (
|
||||||
get_env_value,
|
get_env_value,
|
||||||
get_hermes_home,
|
get_hermes_home,
|
||||||
is_managed,
|
is_managed,
|
||||||
@@ -31,11 +31,11 @@ from hermes_cli.config import (
|
|||||||
)
|
)
|
||||||
# display_hermes_home is imported lazily at call sites to avoid ImportError
|
# 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`.
|
# 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,
|
print_header, print_info, print_success, print_warning, print_error,
|
||||||
prompt, prompt_choice, prompt_yes_no,
|
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] = []
|
pids: list[int] = []
|
||||||
patterns = [
|
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 gateway",
|
||||||
"hermes_cli.main --profile",
|
"hermes_cli.main --profile",
|
||||||
"hermes_cli.main -p",
|
"hermes_cli.main -p",
|
||||||
@@ -303,7 +309,7 @@ def find_gateway_pids(exclude_pids: set | None = None, all_profiles: bool = Fals
|
|||||||
pids: list[int] = []
|
pids: list[int] = []
|
||||||
if not all_profiles:
|
if not all_profiles:
|
||||||
try:
|
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)
|
_append_unique_pid(pids, get_running_pid(), _exclude)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -357,7 +363,7 @@ def get_gateway_runtime_snapshot(system: bool = False) -> GatewayRuntimeSnapshot
|
|||||||
gateway_pids=gateway_pids,
|
gateway_pids=gateway_pids,
|
||||||
)
|
)
|
||||||
|
|
||||||
from hermes_constants import is_container
|
from hermes_agent.constants import is_container
|
||||||
|
|
||||||
if is_linux() and is_container():
|
if is_linux() and is_container():
|
||||||
return GatewayRuntimeSnapshot(
|
return GatewayRuntimeSnapshot(
|
||||||
@@ -445,7 +451,7 @@ def stop_profile_gateway() -> bool:
|
|||||||
Returns True if a process was stopped, False if none was found.
|
Returns True if a process was stopped, False if none was found.
|
||||||
"""
|
"""
|
||||||
try:
|
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:
|
except ImportError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -478,7 +484,7 @@ def is_linux() -> bool:
|
|||||||
return sys.platform.startswith('linux')
|
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:
|
def _wsl_systemd_operational() -> bool:
|
||||||
@@ -552,7 +558,7 @@ def _profile_suffix() -> str:
|
|||||||
"""
|
"""
|
||||||
import hashlib
|
import hashlib
|
||||||
import re
|
import re
|
||||||
from hermes_constants import get_default_hermes_root
|
from hermes_agent.constants import get_default_hermes_root
|
||||||
home = get_hermes_home().resolve()
|
home = get_hermes_home().resolve()
|
||||||
default = get_default_hermes_root().resolve()
|
default = get_default_hermes_root().resolve()
|
||||||
if home == default:
|
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).
|
service definition for a different user (e.g. system service).
|
||||||
"""
|
"""
|
||||||
import re
|
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()
|
home = Path(hermes_home or str(get_hermes_home())).resolve()
|
||||||
default = get_default_hermes_root().resolve()
|
default = get_default_hermes_root().resolve()
|
||||||
if home == default:
|
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.
|
# ExecStart content markers that identify a unit as running our gateway.
|
||||||
# A legacy unit is only flagged when its file contains one of these.
|
# A legacy unit is only flagged when its file contains one of these.
|
||||||
_LEGACY_UNIT_EXECSTART_MARKERS: tuple[str, ...] = (
|
_LEGACY_UNIT_EXECSTART_MARKERS: tuple[str, ...] = (
|
||||||
|
"hermes_agent.cli.main gateway",
|
||||||
|
"hermes_agent/cli/main.py gateway",
|
||||||
"hermes_cli.main gateway",
|
"hermes_cli.main gateway",
|
||||||
"hermes_cli/main.py gateway",
|
"hermes_cli/main.py gateway",
|
||||||
"gateway/run.py",
|
"gateway/run.py",
|
||||||
@@ -1221,7 +1229,7 @@ StartLimitBurst=5
|
|||||||
Type=simple
|
Type=simple
|
||||||
User={username}
|
User={username}
|
||||||
Group={group_name}
|
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}
|
WorkingDirectory={working_dir}
|
||||||
Environment="HOME={home_dir}"
|
Environment="HOME={home_dir}"
|
||||||
Environment="USER={username}"
|
Environment="USER={username}"
|
||||||
@@ -1256,7 +1264,7 @@ StartLimitBurst=5
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
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}
|
WorkingDirectory={working_dir}
|
||||||
Environment="PATH={sane_path}"
|
Environment="PATH={sane_path}"
|
||||||
Environment="VIRTUAL_ENV={venv_dir}"
|
Environment="VIRTUAL_ENV={venv_dir}"
|
||||||
@@ -1501,7 +1509,7 @@ def systemd_restart(system: bool = False):
|
|||||||
if system:
|
if system:
|
||||||
_require_root_for_system_service("restart")
|
_require_root_for_system_service("restart")
|
||||||
refresh_systemd_unit_if_needed(system=system)
|
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()
|
pid = get_running_pid()
|
||||||
if pid is not None and _request_gateway_self_restart(pid):
|
if pid is not None and _request_gateway_self_restart(pid):
|
||||||
@@ -1689,7 +1697,7 @@ def generate_launchd_plist() -> str:
|
|||||||
prog_args = [
|
prog_args = [
|
||||||
f"<string>{python_path}</string>",
|
f"<string>{python_path}</string>",
|
||||||
"<string>-m</string>",
|
"<string>-m</string>",
|
||||||
"<string>hermes_cli.main</string>",
|
"<string>hermes_agent.cli.main</string>",
|
||||||
]
|
]
|
||||||
if profile_arg:
|
if profile_arg:
|
||||||
for part in profile_arg.split():
|
for part in profile_arg.split():
|
||||||
@@ -1799,7 +1807,7 @@ def launchd_install(force: bool = False):
|
|||||||
print()
|
print()
|
||||||
print("Next steps:")
|
print("Next steps:")
|
||||||
print(" hermes gateway status # Check status")
|
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")
|
print(f" tail -f {_dhh()}/logs/gateway.log # View logs")
|
||||||
|
|
||||||
def launchd_uninstall():
|
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.
|
force_after: Seconds of graceful waiting before escalating to force-kill.
|
||||||
"""
|
"""
|
||||||
import time
|
import time
|
||||||
from gateway.status import get_running_pid
|
from hermes_agent.gateway.status import get_running_pid
|
||||||
|
|
||||||
deadline = time.monotonic() + timeout
|
deadline = time.monotonic() + timeout
|
||||||
force_deadline = (time.monotonic() + force_after) if force_after is not None else None
|
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()
|
label = get_launchd_label()
|
||||||
target = f"{_launchd_domain()}/{label}"
|
target = f"{_launchd_domain()}/{label}"
|
||||||
drain_timeout = _get_restart_drain_timeout()
|
drain_timeout = _get_restart_drain_timeout()
|
||||||
from gateway.status import get_running_pid
|
from hermes_agent.gateway.status import get_running_pid
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pid = get_running_pid()
|
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
|
This prevents systemd restart loops when the old process
|
||||||
hasn't fully exited yet.
|
hasn't fully exited yet.
|
||||||
"""
|
"""
|
||||||
sys.path.insert(0, str(PROJECT_ROOT))
|
from hermes_agent.gateway.run import start_gateway
|
||||||
|
|
||||||
from gateway.run import start_gateway
|
|
||||||
|
|
||||||
print("┌─────────────────────────────────────────────────────────┐")
|
print("┌─────────────────────────────────────────────────────────┐")
|
||||||
print("│ ⚕ Hermes Gateway Starting... │")
|
print("│ ⚕ Hermes Gateway Starting... │")
|
||||||
@@ -2430,7 +2436,7 @@ def _platform_status(platform: dict) -> str:
|
|||||||
def _runtime_health_lines() -> list[str]:
|
def _runtime_health_lines() -> list[str]:
|
||||||
"""Summarize the latest persisted gateway runtime health state."""
|
"""Summarize the latest persisted gateway runtime health state."""
|
||||||
try:
|
try:
|
||||||
from gateway.status import read_runtime_status
|
from hermes_agent.gateway.status import read_runtime_status
|
||||||
except Exception:
|
except Exception:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -2562,7 +2568,7 @@ def _setup_standard_platform(platform: dict):
|
|||||||
|
|
||||||
def _setup_whatsapp():
|
def _setup_whatsapp():
|
||||||
"""Delegate to the existing WhatsApp setup flow."""
|
"""Delegate to the existing WhatsApp setup flow."""
|
||||||
from hermes_cli.main import cmd_whatsapp
|
from hermes_agent.cli.main import cmd_whatsapp
|
||||||
import argparse
|
import argparse
|
||||||
cmd_whatsapp(argparse.Namespace())
|
cmd_whatsapp(argparse.Namespace())
|
||||||
|
|
||||||
@@ -2581,7 +2587,7 @@ def _setup_sms():
|
|||||||
|
|
||||||
def _setup_dingtalk():
|
def _setup_dingtalk():
|
||||||
"""Configure DingTalk — QR scan (recommended) or manual credential entry."""
|
"""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,
|
prompt_choice, prompt_yes_no, print_info, print_success, print_warning,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -2612,7 +2618,7 @@ def _setup_dingtalk():
|
|||||||
if method == 0:
|
if method == 0:
|
||||||
# ── QR-code device-flow authorization ──
|
# ── QR-code device-flow authorization ──
|
||||||
try:
|
try:
|
||||||
from hermes_cli.dingtalk_auth import dingtalk_qr_auth
|
from hermes_agent.cli.auth.dingtalk import dingtalk_qr_auth
|
||||||
except ImportError as exc:
|
except ImportError as exc:
|
||||||
print_warning(f" QR auth module failed to load ({exc}), falling back to manual input.")
|
print_warning(f" QR auth module failed to load ({exc}), falling back to manual input.")
|
||||||
_setup_standard_platform(dingtalk_platform)
|
_setup_standard_platform(dingtalk_platform)
|
||||||
@@ -2644,6 +2650,12 @@ def _setup_wecom():
|
|||||||
_setup_standard_platform(wecom_platform)
|
_setup_standard_platform(wecom_platform)
|
||||||
|
|
||||||
|
|
||||||
|
def _setup_wecom_callback():
|
||||||
|
"""Configure WeCom Callback (self-built app) via the standard platform setup."""
|
||||||
|
wecom_platform = next(p for p in _PLATFORMS if p["key"] == "wecom_callback")
|
||||||
|
_setup_standard_platform(wecom_platform)
|
||||||
|
|
||||||
|
|
||||||
def _is_service_installed() -> bool:
|
def _is_service_installed() -> bool:
|
||||||
"""Check if the gateway is installed as a system service."""
|
"""Check if the gateway is installed as a system service."""
|
||||||
if supports_systemd_services():
|
if supports_systemd_services():
|
||||||
@@ -2714,7 +2726,7 @@ def _setup_weixin():
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
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:
|
except Exception as exc:
|
||||||
print_error(f" Weixin adapter import failed: {exc}")
|
print_error(f" Weixin adapter import failed: {exc}")
|
||||||
print_info(" Install gateway dependencies first, then retry.")
|
print_info(" Install gateway dependencies first, then retry.")
|
||||||
@@ -2849,7 +2861,7 @@ def _setup_feishu():
|
|||||||
if method_idx == 0:
|
if method_idx == 0:
|
||||||
# ── QR scan-to-create ──
|
# ── QR scan-to-create ──
|
||||||
try:
|
try:
|
||||||
from gateway.platforms.feishu import qr_register
|
from hermes_agent.gateway.platforms.feishu import qr_register
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print_error(f" Feishu / Lark onboard import failed: {exc}")
|
print_error(f" Feishu / Lark onboard import failed: {exc}")
|
||||||
qr_register = None
|
qr_register = None
|
||||||
@@ -2890,7 +2902,7 @@ def _setup_feishu():
|
|||||||
# Try to probe the bot with manual credentials
|
# Try to probe the bot with manual credentials
|
||||||
bot_name = None
|
bot_name = None
|
||||||
try:
|
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)
|
bot_info = probe_bot(app_id, app_secret, domain)
|
||||||
if bot_info:
|
if bot_info:
|
||||||
bot_name = bot_info.get("bot_name")
|
bot_name = bot_info.get("bot_name")
|
||||||
@@ -3123,11 +3135,11 @@ def _qqbot_qr_flow():
|
|||||||
or None on failure/cancel.
|
or None on failure/cancel.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from gateway.platforms.qqbot import (
|
from hermes_agent.gateway.platforms.qqbot import (
|
||||||
create_bind_task, poll_bind_result, build_connect_url,
|
create_bind_task, poll_bind_result, build_connect_url,
|
||||||
decrypt_secret, BindStatus,
|
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:
|
except Exception as exc:
|
||||||
print_error(f" QQBot onboard import failed: {exc}")
|
print_error(f" QQBot onboard import failed: {exc}")
|
||||||
return None
|
return None
|
||||||
@@ -3465,7 +3477,7 @@ def gateway_setup():
|
|||||||
print_info(" To enable systemd: add systemd=true to /etc/wsl.conf, then 'wsl --shutdown'")
|
print_info(" To enable systemd: add systemd=true to /etc/wsl.conf, then 'wsl --shutdown'")
|
||||||
else:
|
else:
|
||||||
if is_termux():
|
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(" Termux does not use systemd/launchd services.")
|
||||||
print_info(" Run in foreground: hermes gateway run")
|
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 &")
|
print_info(f" Or start it manually in the background (best effort): nohup hermes gateway run >{_dhh()}/logs/gateway.log 2>&1 &")
|
||||||
@@ -50,8 +50,8 @@ def hooks_command(args) -> None:
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
def _cmd_list(_args) -> None:
|
def _cmd_list(_args) -> None:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
from agent import shell_hooks
|
from hermes_agent.agent import shell_hooks
|
||||||
|
|
||||||
specs = shell_hooks.iter_configured_hooks(load_config())
|
specs = shell_hooks.iter_configured_hooks(load_config())
|
||||||
|
|
||||||
@@ -186,9 +186,9 @@ _DEFAULT_PAYLOADS = {
|
|||||||
|
|
||||||
|
|
||||||
def _cmd_test(args) -> None:
|
def _cmd_test(args) -> None:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
from hermes_cli.plugins import VALID_HOOKS
|
from hermes_agent.cli.plugins import VALID_HOOKS
|
||||||
from agent import shell_hooks
|
from hermes_agent.agent import shell_hooks
|
||||||
|
|
||||||
event = args.event
|
event = args.event
|
||||||
if event not in VALID_HOOKS:
|
if event not in VALID_HOOKS:
|
||||||
@@ -273,7 +273,7 @@ def _truncate(s: str, n: int) -> str:
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
def _cmd_revoke(args) -> None:
|
def _cmd_revoke(args) -> None:
|
||||||
from agent import shell_hooks
|
from hermes_agent.agent import shell_hooks
|
||||||
|
|
||||||
removed = shell_hooks.revoke(args.command)
|
removed = shell_hooks.revoke(args.command)
|
||||||
if removed == 0:
|
if removed == 0:
|
||||||
@@ -291,8 +291,8 @@ def _cmd_revoke(args) -> None:
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
def _cmd_doctor(_args) -> None:
|
def _cmd_doctor(_args) -> None:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
from agent import shell_hooks
|
from hermes_agent.agent import shell_hooks
|
||||||
|
|
||||||
specs = shell_hooks.iter_configured_hooks(load_config())
|
specs = shell_hooks.iter_configured_hooks(load_config())
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ from datetime import datetime, timedelta
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Sequence
|
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)
|
# Known log files (name → filename)
|
||||||
LOG_FILES = {
|
LOG_FILES = {
|
||||||
@@ -191,7 +191,7 @@ def tail_log(
|
|||||||
# Resolve component to logger name prefixes
|
# Resolve component to logger name prefixes
|
||||||
component_prefixes = None
|
component_prefixes = None
|
||||||
if component:
|
if component:
|
||||||
from hermes_logging import COMPONENT_PREFIXES
|
from hermes_agent.logging import COMPONENT_PREFIXES
|
||||||
component_lower = component.lower()
|
component_lower = component.lower()
|
||||||
if component_lower not in COMPONENT_PREFIXES:
|
if component_lower not in COMPONENT_PREFIXES:
|
||||||
available = ", ".join(sorted(COMPONENT_PREFIXES))
|
available = ", ".join(sorted(COMPONENT_PREFIXES))
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -15,15 +15,15 @@ import re
|
|||||||
import time
|
import time
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
from hermes_cli.config import (
|
from hermes_agent.cli.config import (
|
||||||
load_config,
|
load_config,
|
||||||
save_config,
|
save_config,
|
||||||
get_env_value,
|
get_env_value,
|
||||||
save_env_value,
|
save_env_value,
|
||||||
get_hermes_home, # noqa: F401 — used by test mocks
|
get_hermes_home, # noqa: F401 — used by test mocks
|
||||||
)
|
)
|
||||||
from hermes_cli.colors import Colors, color
|
from hermes_agent.cli.ui.colors import Colors, color
|
||||||
from hermes_constants import display_hermes_home
|
from hermes_agent.constants import display_hermes_home
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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:
|
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)
|
return _shared_prompt(question, default=default, password=password)
|
||||||
|
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ def _probe_single_server(
|
|||||||
Returns list of ``(tool_name, description)`` tuples.
|
Returns list of ``(tool_name, description)`` tuples.
|
||||||
Raises on connection failure.
|
Raises on connection failure.
|
||||||
"""
|
"""
|
||||||
from tools.mcp_tool import (
|
from hermes_agent.tools.mcp.tool import (
|
||||||
_ensure_mcp_loop,
|
_ensure_mcp_loop,
|
||||||
_run_on_mcp_loop,
|
_run_on_mcp_loop,
|
||||||
_connect_server,
|
_connect_server,
|
||||||
@@ -279,7 +279,7 @@ def cmd_mcp_add(args):
|
|||||||
_info(f"Starting OAuth flow for '{name}'...")
|
_info(f"Starting OAuth flow for '{name}'...")
|
||||||
oauth_ok = False
|
oauth_ok = False
|
||||||
try:
|
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)
|
oauth_auth = get_manager().get_or_build_provider(name, url, None)
|
||||||
if oauth_auth:
|
if oauth_auth:
|
||||||
server_config["auth"] = "oauth"
|
server_config["auth"] = "oauth"
|
||||||
@@ -372,7 +372,7 @@ def cmd_mcp_add(args):
|
|||||||
|
|
||||||
if choice in ("s", "select"):
|
if choice in ("s", "select"):
|
||||||
# Interactive tool selection
|
# 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]
|
labels = [f"{t[0]} — {t[1]}" for t in tools]
|
||||||
pre_selected = set(range(len(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
|
# any provider instance cached in the current process (e.g. from an
|
||||||
# earlier `hermes mcp test` in the same session) is evicted too.
|
# earlier `hermes mcp test` in the same session) is evicted too.
|
||||||
try:
|
try:
|
||||||
from tools.mcp_oauth_manager import get_manager
|
from hermes_agent.tools.mcp.oauth_manager import get_manager
|
||||||
get_manager().remove(name)
|
get_manager().remove(name)
|
||||||
_success("Cleaned up OAuth tokens")
|
_success("Cleaned up OAuth tokens")
|
||||||
except Exception:
|
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
|
# Wipe both disk and in-memory cache so the next probe forces a fresh
|
||||||
# OAuth flow.
|
# OAuth flow.
|
||||||
try:
|
try:
|
||||||
from tools.mcp_oauth_manager import get_manager
|
from hermes_agent.tools.mcp.oauth_manager import get_manager
|
||||||
mgr = get_manager()
|
mgr = get_manager()
|
||||||
mgr.remove(name)
|
mgr.remove(name)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -700,7 +700,7 @@ def cmd_mcp_configure(args):
|
|||||||
print()
|
print()
|
||||||
|
|
||||||
# Interactive checklist
|
# 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]
|
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)
|
action = getattr(args, "mcp_action", None)
|
||||||
|
|
||||||
if action == "serve":
|
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))
|
run_mcp_server(verbose=getattr(args, "verbose", False))
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
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.
|
items: list of (label, description) tuples.
|
||||||
Returns selected index, or default on escape/quit.
|
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
|
# Format (label, desc) tuples into display strings
|
||||||
display_items = [
|
display_items = [
|
||||||
f"{label} {desc}" if desc else label
|
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:
|
def _install_dependencies(provider_name: str) -> None:
|
||||||
"""Install pip dependencies declared in plugin.yaml."""
|
"""Install pip dependencies declared in plugin.yaml."""
|
||||||
import subprocess
|
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)
|
plugin_dir = find_provider_dir(provider_name)
|
||||||
if not plugin_dir:
|
if not plugin_dir:
|
||||||
@@ -148,7 +148,7 @@ def _get_available_providers() -> list:
|
|||||||
Returns list of (name, description, provider_instance) tuples.
|
Returns list of (name, description, provider_instance) tuples.
|
||||||
"""
|
"""
|
||||||
try:
|
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()
|
raw = discover_memory_providers()
|
||||||
except Exception:
|
except Exception:
|
||||||
raw = []
|
raw = []
|
||||||
@@ -184,7 +184,7 @@ def _get_available_providers() -> list:
|
|||||||
|
|
||||||
def cmd_setup_provider(provider_name: str) -> None:
|
def cmd_setup_provider(provider_name: str) -> None:
|
||||||
"""Run memory setup for a specific provider, skipping the picker."""
|
"""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()
|
providers = _get_available_providers()
|
||||||
match = None
|
match = None
|
||||||
@@ -220,7 +220,7 @@ def cmd_setup_provider(provider_name: str) -> None:
|
|||||||
|
|
||||||
def cmd_setup(args) -> None:
|
def cmd_setup(args) -> None:
|
||||||
"""Interactive memory provider setup wizard."""
|
"""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()
|
providers = _get_available_providers()
|
||||||
|
|
||||||
@@ -386,7 +386,7 @@ def _write_env_vars(env_path: Path, env_writes: dict) -> None:
|
|||||||
|
|
||||||
def cmd_status(args) -> None:
|
def cmd_status(args) -> None:
|
||||||
"""Show current memory provider config."""
|
"""Show current memory provider config."""
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
|
|
||||||
config = load_config()
|
config = load_config()
|
||||||
mem_config = config.get("memory", {})
|
mem_config = config.get("memory", {})
|
||||||
0
hermes_agent/cli/models/__init__.py
Normal file
0
hermes_agent/cli/models/__init__.py
Normal file
@@ -16,7 +16,7 @@ from difflib import get_close_matches
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, NamedTuple, Optional
|
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
|
# Identify ourselves so endpoints fronted by Cloudflare's Browser Integrity
|
||||||
# Check (error 1010) don't reject the default ``Python-urllib/*`` signature.
|
# 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`
|
This keeps the gateway /model picker in sync with the CLI `hermes model`
|
||||||
flow without maintaining a separate static list.
|
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))
|
return _add_forward_compat_models(list(DEFAULT_CODEX_MODELS))
|
||||||
|
|
||||||
|
|
||||||
@@ -488,7 +488,7 @@ def check_nous_free_tier() -> bool:
|
|||||||
return cached_result
|
return cached_result
|
||||||
|
|
||||||
try:
|
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)
|
# Ensure we have a fresh token (triggers refresh if needed)
|
||||||
resolve_nous_runtime_credentials(min_key_ttl_seconds=60)
|
resolve_nous_runtime_credentials(min_key_ttl_seconds=60)
|
||||||
@@ -583,7 +583,7 @@ def fetch_nous_recommended_models(
|
|||||||
def _resolve_nous_portal_url() -> str:
|
def _resolve_nous_portal_url() -> str:
|
||||||
"""Best-effort lookup of the Portal base URL the user is authed against."""
|
"""Best-effort lookup of the Portal base URL the user is authed against."""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.auth import (
|
from hermes_agent.cli.auth.auth import (
|
||||||
DEFAULT_NOUS_PORTAL_URL,
|
DEFAULT_NOUS_PORTAL_URL,
|
||||||
get_provider_auth_state,
|
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:
|
if _ai_gateway_catalog_cache is not None and not force_refresh:
|
||||||
return list(_ai_gateway_catalog_cache)
|
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)
|
fallback = list(VERCEL_AI_GATEWAY_MODELS)
|
||||||
preferred_ids = [mid for mid, _ in fallback]
|
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
|
``prompt`` / ``completion``. This translates. Cache read/write field names
|
||||||
already match.
|
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("/")
|
cache_key = AI_GATEWAY_BASE_URL.rstrip("/")
|
||||||
if not force_refresh and cache_key in _pricing_cache:
|
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]:
|
def _resolve_nous_pricing_credentials() -> tuple[str, str]:
|
||||||
"""Return ``(api_key, base_url)`` for Nous Portal pricing, or empty strings."""
|
"""Return ``(api_key, base_url)`` for Nous Portal pricing, or empty strings."""
|
||||||
try:
|
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()
|
creds = resolve_nous_runtime_credentials()
|
||||||
if creds:
|
if creds:
|
||||||
return (creds.get("api_key", ""), creds.get("base_url", ""))
|
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
|
# Check if this provider has credentials available
|
||||||
has_creds = False
|
has_creds = False
|
||||||
try:
|
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":
|
if pid == "custom":
|
||||||
custom_base_url = _get_custom_base_url() or ""
|
custom_base_url = _get_custom_base_url() or ""
|
||||||
has_creds = bool(custom_base_url.strip())
|
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:
|
def _get_custom_base_url() -> str:
|
||||||
"""Get the custom endpoint base_url from config.yaml."""
|
"""Get the custom endpoint base_url from config.yaml."""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
config = load_config()
|
config = load_config()
|
||||||
model_cfg = config.get("model", {})
|
model_cfg = config.get("model", {})
|
||||||
if isinstance(model_cfg, dict):
|
if isinstance(model_cfg, dict):
|
||||||
@@ -1401,7 +1401,7 @@ def detect_provider_for_model(
|
|||||||
# credential pool, or auth store entries.
|
# credential pool, or auth store entries.
|
||||||
has_creds = False
|
has_creds = False
|
||||||
try:
|
try:
|
||||||
from hermes_cli.auth import PROVIDER_REGISTRY
|
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
|
||||||
pconfig = PROVIDER_REGISTRY.get(direct_match)
|
pconfig = PROVIDER_REGISTRY.get(direct_match)
|
||||||
if pconfig:
|
if pconfig:
|
||||||
for env_var in pconfig.api_key_env_vars:
|
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).
|
# Claude Code tokens, and other non-env-var credentials (#10300).
|
||||||
if not has_creds:
|
if not has_creds:
|
||||||
try:
|
try:
|
||||||
from agent.credential_pool import load_pool
|
from hermes_agent.providers.credential_pool import load_pool
|
||||||
pool = load_pool(direct_match)
|
pool = load_pool(direct_match)
|
||||||
if pool.has_credentials():
|
if pool.has_credentials():
|
||||||
has_creds = True
|
has_creds = True
|
||||||
@@ -1422,7 +1422,7 @@ def detect_provider_for_model(
|
|||||||
pass
|
pass
|
||||||
if not has_creds:
|
if not has_creds:
|
||||||
try:
|
try:
|
||||||
from hermes_cli.auth import _load_auth_store
|
from hermes_agent.cli.auth.auth import _load_auth_store
|
||||||
store = _load_auth_store()
|
store = _load_auth_store()
|
||||||
if direct_match in store.get("providers", {}) or direct_match in store.get("credential_pool", {}):
|
if direct_match in store.get("providers", {}) or direct_match in store.get("credential_pool", {}):
|
||||||
has_creds = True
|
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:
|
def _resolve_copilot_catalog_api_key() -> str:
|
||||||
"""Best-effort GitHub token for fetching the Copilot model catalog."""
|
"""Best-effort GitHub token for fetching the Copilot model catalog."""
|
||||||
try:
|
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")
|
creds = resolve_api_key_provider_credentials("copilot")
|
||||||
return str(creds.get("api_key") or "").strip()
|
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":
|
if normalized == "openrouter":
|
||||||
return model_ids(force_refresh=force_refresh)
|
return model_ids(force_refresh=force_refresh)
|
||||||
if normalized == "openai-codex":
|
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()
|
return get_codex_model_ids()
|
||||||
if normalized in {"copilot", "copilot-acp"}:
|
if normalized in {"copilot", "copilot-acp"}:
|
||||||
@@ -1605,7 +1605,7 @@ def provider_model_ids(provider: Optional[str], *, force_refresh: bool = False)
|
|||||||
if normalized == "nous":
|
if normalized == "nous":
|
||||||
# Try live Nous Portal /models endpoint
|
# Try live Nous Portal /models endpoint
|
||||||
try:
|
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()
|
creds = resolve_nous_runtime_credentials()
|
||||||
if creds:
|
if creds:
|
||||||
live = fetch_nous_models(api_key=creds.get("api_key", ""), inference_base_url=creds.get("base_url", ""))
|
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.
|
Claude Code auto-discovery). Returns sorted model IDs or None.
|
||||||
"""
|
"""
|
||||||
try:
|
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:
|
except ImportError:
|
||||||
return None
|
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"}
|
headers: dict[str, str] = {"anthropic-version": "2023-06-01"}
|
||||||
if _is_oauth_token(token):
|
if _is_oauth_token(token):
|
||||||
headers["Authorization"] = f"Bearer {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)
|
headers["anthropic-beta"] = ",".join(_COMMON_BETAS + _OAUTH_ONLY_BETAS)
|
||||||
else:
|
else:
|
||||||
headers["x-api-key"] = token
|
headers["x-api-key"] = token
|
||||||
@@ -1701,7 +1701,7 @@ def copilot_default_headers() -> dict[str, str]:
|
|||||||
Copilot CLI send on every request.
|
Copilot CLI send on every request.
|
||||||
"""
|
"""
|
||||||
try:
|
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)
|
return copilot_request_headers(is_agent_turn=True)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return {
|
return {
|
||||||
@@ -2117,7 +2117,7 @@ def _fetch_ai_gateway_models(timeout: float = 5.0) -> Optional[list[str]]:
|
|||||||
return None
|
return None
|
||||||
base_url = os.getenv("AI_GATEWAY_BASE_URL", "").strip()
|
base_url = os.getenv("AI_GATEWAY_BASE_URL", "").strip()
|
||||||
if not base_url:
|
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
|
base_url = AI_GATEWAY_BASE_URL
|
||||||
|
|
||||||
url = base_url.rstrip("/") + "/models"
|
url = base_url.rstrip("/") + "/models"
|
||||||
@@ -2161,7 +2161,7 @@ _OLLAMA_CLOUD_CACHE_TTL = 3600 # 1 hour
|
|||||||
|
|
||||||
def _ollama_cloud_cache_path() -> Path:
|
def _ollama_cloud_cache_path() -> Path:
|
||||||
"""Return the path for the Ollama Cloud model cache."""
|
"""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"
|
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:
|
def _save_ollama_cloud_cache(models: list[str]) -> None:
|
||||||
"""Persist the merged Ollama Cloud model list to disk."""
|
"""Persist the merged Ollama Cloud model list to disk."""
|
||||||
try:
|
try:
|
||||||
from utils import atomic_json_write
|
from hermes_agent.utils import atomic_json_write
|
||||||
cache_path = _ollama_cloud_cache_path()
|
cache_path = _ollama_cloud_cache_path()
|
||||||
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
atomic_json_write(cache_path, {"models": models, "cached_at": time.time()}, indent=None)
|
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
|
# 3. models.dev registry
|
||||||
mdev_models: list[str] = []
|
mdev_models: list[str] = []
|
||||||
try:
|
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")
|
mdev_models = list_agentic_models("ollama-cloud")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -2510,7 +2510,7 @@ def validate_requested_model(
|
|||||||
# AWS SDK control plane (ListFoundationModels + ListInferenceProfiles).
|
# AWS SDK control plane (ListFoundationModels + ListInferenceProfiles).
|
||||||
if normalized == "bedrock":
|
if normalized == "bedrock":
|
||||||
try:
|
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()
|
region = resolve_bedrock_region()
|
||||||
discovered = discover_bedrock_models(region)
|
discovered = discover_bedrock_models(region)
|
||||||
discovered_ids = {m["id"] for m in discovered}
|
discovered_ids = {m["id"] for m in discovered}
|
||||||
@@ -184,7 +184,7 @@ def _normalize_provider_alias(provider_name: str) -> str:
|
|||||||
if not raw:
|
if not raw:
|
||||||
return raw
|
return raw
|
||||||
try:
|
try:
|
||||||
from hermes_cli.models import normalize_provider
|
from hermes_agent.cli.models.models import normalize_provider
|
||||||
|
|
||||||
return normalize_provider(raw)
|
return normalize_provider(raw)
|
||||||
except Exception:
|
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.
|
# HTTP 400 "model_not_supported". See issue #6879.
|
||||||
if provider in {"copilot", "copilot-acp"}:
|
if provider in {"copilot", "copilot-acp"}:
|
||||||
try:
|
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)
|
normalized = normalize_copilot_model_id(name)
|
||||||
if normalized:
|
if normalized:
|
||||||
@@ -25,17 +25,17 @@ import re
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import List, NamedTuple, Optional
|
from typing import List, NamedTuple, Optional
|
||||||
|
|
||||||
from hermes_cli.providers import (
|
from hermes_agent.cli.providers import (
|
||||||
custom_provider_slug,
|
custom_provider_slug,
|
||||||
determine_api_mode,
|
determine_api_mode,
|
||||||
get_label,
|
get_label,
|
||||||
is_aggregator,
|
is_aggregator,
|
||||||
resolve_provider_full,
|
resolve_provider_full,
|
||||||
)
|
)
|
||||||
from hermes_cli.model_normalize import (
|
from hermes_agent.cli.models.normalize import (
|
||||||
normalize_model_for_provider,
|
normalize_model_for_provider,
|
||||||
)
|
)
|
||||||
from agent.models_dev import (
|
from hermes_agent.providers.metadata_dev import (
|
||||||
ModelCapabilities,
|
ModelCapabilities,
|
||||||
ModelInfo,
|
ModelInfo,
|
||||||
get_model_capabilities,
|
get_model_capabilities,
|
||||||
@@ -193,7 +193,7 @@ def _load_direct_aliases() -> dict[str, DirectAlias]:
|
|||||||
"""
|
"""
|
||||||
merged = dict(_BUILTIN_DIRECT_ALIASES)
|
merged = dict(_BUILTIN_DIRECT_ALIASES)
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
cfg = load_config()
|
cfg = load_config()
|
||||||
user_aliases = cfg.get("model_aliases")
|
user_aliases = cfg.get("model_aliases")
|
||||||
if isinstance(user_aliases, dict):
|
if isinstance(user_aliases, dict):
|
||||||
@@ -456,13 +456,13 @@ def switch_model(
|
|||||||
Returns:
|
Returns:
|
||||||
ModelSwitchResult with all information the caller needs.
|
ModelSwitchResult with all information the caller needs.
|
||||||
"""
|
"""
|
||||||
from hermes_cli.models import (
|
from hermes_agent.cli.models.models import (
|
||||||
copilot_model_api_mode,
|
copilot_model_api_mode,
|
||||||
detect_provider_for_model,
|
detect_provider_for_model,
|
||||||
validate_requested_model,
|
validate_requested_model,
|
||||||
opencode_model_api_mode,
|
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 = ""
|
resolved_alias = ""
|
||||||
new_model = raw_input.strip()
|
new_model = raw_input.strip()
|
||||||
@@ -486,7 +486,7 @@ def switch_model(
|
|||||||
)
|
)
|
||||||
# Check for common config issues that cause provider resolution failures
|
# Check for common config issues that cause provider resolution failures
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import validate_config_structure
|
from hermes_agent.cli.config import validate_config_structure
|
||||||
_cfg_issues = validate_config_structure()
|
_cfg_issues = validate_config_structure()
|
||||||
if _cfg_issues:
|
if _cfg_issues:
|
||||||
_switch_err += "\n\nRun 'hermes doctor' — config issues detected:"
|
_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 no model specified, try auto-detect from endpoint
|
||||||
if not new_model:
|
if not new_model:
|
||||||
if pdef.base_url:
|
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)
|
detected = _auto_detect_local_model(pdef.base_url)
|
||||||
if detected:
|
if detected:
|
||||||
new_model = 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.
|
Only includes providers that have API keys set or are user-defined endpoints.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
from agent.models_dev import (
|
from hermes_agent.providers.metadata_dev import (
|
||||||
PROVIDER_TO_MODELS_DEV,
|
PROVIDER_TO_MODELS_DEV,
|
||||||
fetch_models_dev,
|
fetch_models_dev,
|
||||||
get_provider_info as _mdev_pinfo,
|
get_provider_info as _mdev_pinfo,
|
||||||
)
|
)
|
||||||
from hermes_cli.auth import PROVIDER_REGISTRY
|
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
|
||||||
from hermes_cli.models import OPENROUTER_MODELS, _PROVIDER_MODELS
|
from hermes_agent.cli.models.models import OPENROUTER_MODELS, _PROVIDER_MODELS
|
||||||
|
|
||||||
results: List[dict] = []
|
results: List[dict] = []
|
||||||
seen_slugs: set = set() # lowercase-normalized to catch case variants (#9545)
|
seen_slugs: set = set() # lowercase-normalized to catch case variants (#9545)
|
||||||
@@ -826,7 +826,7 @@ def list_authenticated_providers(
|
|||||||
curated["nous"] = curated["openrouter"]
|
curated["nous"] = curated["openrouter"]
|
||||||
# Ollama Cloud uses dynamic discovery (no static curated list)
|
# Ollama Cloud uses dynamic discovery (no static curated list)
|
||||||
if "ollama-cloud" not in curated:
|
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()
|
curated["ollama-cloud"] = fetch_ollama_cloud_models()
|
||||||
|
|
||||||
# --- 1. Check Hermes-mapped providers ---
|
# --- 1. Check Hermes-mapped providers ---
|
||||||
@@ -878,8 +878,8 @@ def list_authenticated_providers(
|
|||||||
seen_mdev_ids.add(mdev_id)
|
seen_mdev_ids.add(mdev_id)
|
||||||
|
|
||||||
# --- 2. Check Hermes-only providers (nous, openai-codex, copilot, opencode-go) ---
|
# --- 2. Check Hermes-only providers (nous, openai-codex, copilot, opencode-go) ---
|
||||||
from hermes_cli.providers import HERMES_OVERLAYS
|
from hermes_agent.cli.providers import HERMES_OVERLAYS
|
||||||
from hermes_cli.auth import PROVIDER_REGISTRY as _auth_registry
|
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY as _auth_registry
|
||||||
|
|
||||||
# Build reverse mapping: models.dev ID → Hermes provider ID.
|
# Build reverse mapping: models.dev ID → Hermes provider ID.
|
||||||
# HERMES_OVERLAYS keys may be models.dev IDs (e.g. "github-copilot")
|
# 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).
|
# OAuth via external credential files).
|
||||||
if not has_creds:
|
if not has_creds:
|
||||||
try:
|
try:
|
||||||
from hermes_cli.auth import _load_auth_store
|
from hermes_agent.cli.auth.auth import _load_auth_store
|
||||||
store = _load_auth_store()
|
store = _load_auth_store()
|
||||||
providers_store = store.get("providers", {})
|
providers_store = store.get("providers", {})
|
||||||
pool_store = store.get("credential_pool", {})
|
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.
|
# imports on demand but aren't in the raw auth.json yet.
|
||||||
if not has_creds:
|
if not has_creds:
|
||||||
try:
|
try:
|
||||||
from agent.credential_pool import load_pool
|
from hermes_agent.providers.credential_pool import load_pool
|
||||||
pool = load_pool(hermes_slug)
|
pool = load_pool(hermes_slug)
|
||||||
if pool.has_credentials():
|
if pool.has_credentials():
|
||||||
has_creds = True
|
has_creds = True
|
||||||
@@ -945,7 +945,7 @@ def list_authenticated_providers(
|
|||||||
# configured.
|
# configured.
|
||||||
if not has_creds and hermes_slug == "anthropic":
|
if not has_creds and hermes_slug == "anthropic":
|
||||||
try:
|
try:
|
||||||
from agent.anthropic_adapter import (
|
from hermes_agent.providers.anthropic_adapter import (
|
||||||
read_claude_code_credentials,
|
read_claude_code_credentials,
|
||||||
read_hermes_oauth_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
|
# in PROVIDER_TO_MODELS_DEV or HERMES_OVERLAYS (keeps /model in sync
|
||||||
# with `hermes model`).
|
# with `hermes model`).
|
||||||
try:
|
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:
|
except ImportError:
|
||||||
_canon_provs = []
|
_canon_provs = []
|
||||||
|
|
||||||
@@ -997,7 +997,7 @@ def list_authenticated_providers(
|
|||||||
# Also check auth store and credential pool
|
# Also check auth store and credential pool
|
||||||
if not _cp_has_creds:
|
if not _cp_has_creds:
|
||||||
try:
|
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_store = _load_auth_store()
|
||||||
_cp_providers_store = _cp_store.get("providers", {})
|
_cp_providers_store = _cp_store.get("providers", {})
|
||||||
_cp_pool_store = _cp_store.get("credential_pool", {})
|
_cp_pool_store = _cp_store.get("credential_pool", {})
|
||||||
@@ -1010,7 +1010,7 @@ def list_authenticated_providers(
|
|||||||
pass
|
pass
|
||||||
if not _cp_has_creds:
|
if not _cp_has_creds:
|
||||||
try:
|
try:
|
||||||
from agent.credential_pool import load_pool
|
from hermes_agent.providers.credential_pool import load_pool
|
||||||
_cp_pool = load_pool(_cp.slug)
|
_cp_pool = load_pool(_cp.slug)
|
||||||
if _cp_pool.has_credentials():
|
if _cp_pool.has_credentials():
|
||||||
_cp_has_creds = True
|
_cp_has_creds = True
|
||||||
@@ -6,10 +6,10 @@ from dataclasses import dataclass
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Iterable, Optional, Set
|
from typing import Dict, Iterable, Optional, Set
|
||||||
|
|
||||||
from hermes_cli.auth import get_nous_auth_status
|
from hermes_agent.cli.auth.auth import get_nous_auth_status
|
||||||
from hermes_cli.config import get_env_value, load_config
|
from hermes_agent.cli.config import get_env_value, load_config
|
||||||
from tools.managed_tool_gateway import is_managed_tool_gateway_ready
|
from hermes_agent.tools.managed_gateway import is_managed_tool_gateway_ready
|
||||||
from tools.tool_backend_helpers import (
|
from hermes_agent.tools.backend_helpers import (
|
||||||
fal_key_is_configured,
|
fal_key_is_configured,
|
||||||
has_direct_modal_credentials,
|
has_direct_modal_credentials,
|
||||||
managed_nous_tools_enabled,
|
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:
|
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")
|
platform_toolsets = config.get("platform_toolsets")
|
||||||
if not isinstance(platform_toolsets, dict) or not platform_toolsets:
|
if not isinstance(platform_toolsets, dict) or not platform_toolsets:
|
||||||
@@ -123,7 +123,7 @@ def _has_agent_browser() -> bool:
|
|||||||
|
|
||||||
agent_browser_bin = shutil.which("agent-browser")
|
agent_browser_bin = shutil.which("agent-browser")
|
||||||
local_bin = (
|
local_bin = (
|
||||||
Path(__file__).parent.parent / "node_modules" / ".bin" / "agent-browser"
|
Path(__file__).resolve().parents[2] / "node_modules" / ".bin" / "agent-browser"
|
||||||
)
|
)
|
||||||
return bool(agent_browser_bin or local_bin.exists())
|
return bool(agent_browser_bin or local_bin.exists())
|
||||||
|
|
||||||
@@ -688,7 +688,7 @@ def prompt_enable_tool_gateway(config: Dict[str, object]) -> set[str]:
|
|||||||
return set()
|
return set()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from hermes_cli.setup import prompt_choice
|
from hermes_agent.cli.setup_wizard import prompt_choice
|
||||||
except Exception:
|
except Exception:
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
@@ -766,7 +766,7 @@ def prompt_enable_tool_gateway(config: Dict[str, object]) -> set[str]:
|
|||||||
|
|
||||||
changed = apply_gateway_defaults(config, to_apply)
|
changed = apply_gateway_defaults(config, to_apply)
|
||||||
if changed:
|
if changed:
|
||||||
from hermes_cli.config import save_config
|
from hermes_agent.cli.config import save_config
|
||||||
save_config(config)
|
save_config(config)
|
||||||
# Only report the tools that actually switched (not already-managed ones)
|
# Only report the tools that actually switched (not already-managed ones)
|
||||||
newly_switched = changed - set(already_managed)
|
newly_switched = changed - set(already_managed)
|
||||||
@@ -10,7 +10,7 @@ Usage:
|
|||||||
|
|
||||||
def pairing_command(args):
|
def pairing_command(args):
|
||||||
"""Handle hermes pairing subcommands."""
|
"""Handle hermes pairing subcommands."""
|
||||||
from gateway.pairing import PairingStore
|
from hermes_agent.gateway.pairing import PairingStore
|
||||||
|
|
||||||
store = PairingStore()
|
store = PairingStore()
|
||||||
action = getattr(args, "pairing_action", None)
|
action = getattr(args, "pairing_action", None)
|
||||||
@@ -43,8 +43,8 @@ from dataclasses import dataclass, field
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, Dict, List, Optional, Set, Union
|
from typing import Any, Callable, Dict, List, Optional, Set, Union
|
||||||
|
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_agent.constants import get_hermes_home
|
||||||
from utils import env_var_enabled
|
from hermes_agent.utils import env_var_enabled
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import yaml
|
import yaml
|
||||||
@@ -73,7 +73,7 @@ VALID_HOOKS: Set[str] = {
|
|||||||
"subagent_stop",
|
"subagent_stop",
|
||||||
}
|
}
|
||||||
|
|
||||||
ENTRY_POINTS_GROUP = "hermes_agent.plugins"
|
ENTRY_POINTS_GROUP = "plugins"
|
||||||
|
|
||||||
_NS_PARENT = "hermes_plugins"
|
_NS_PARENT = "hermes_plugins"
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ def _get_disabled_plugins() -> set:
|
|||||||
``plugins.enabled``.
|
``plugins.enabled``.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
config = load_config()
|
config = load_config()
|
||||||
disabled = config.get("plugins", {}).get("disabled", [])
|
disabled = config.get("plugins", {}).get("disabled", [])
|
||||||
return set(disabled) if isinstance(disabled, list) else set()
|
return set(disabled) if isinstance(disabled, list) else set()
|
||||||
@@ -114,7 +114,7 @@ def _get_enabled_plugins() -> Optional[set]:
|
|||||||
* ``set(...)`` — the concrete allow-list.
|
* ``set(...)`` — the concrete allow-list.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
config = load_config()
|
config = load_config()
|
||||||
plugins_cfg = config.get("plugins")
|
plugins_cfg = config.get("plugins")
|
||||||
if not isinstance(plugins_cfg, dict):
|
if not isinstance(plugins_cfg, dict):
|
||||||
@@ -207,7 +207,7 @@ class PluginContext:
|
|||||||
emoji: str = "",
|
emoji: str = "",
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Register a tool in the global registry **and** track it as plugin-provided."""
|
"""Register a tool in the global registry **and** track it as plugin-provided."""
|
||||||
from tools.registry import registry
|
from hermes_agent.tools.registry import registry
|
||||||
|
|
||||||
registry.register(
|
registry.register(
|
||||||
name=name,
|
name=name,
|
||||||
@@ -305,7 +305,7 @@ class PluginContext:
|
|||||||
|
|
||||||
# Reject if it conflicts with a built-in command
|
# Reject if it conflicts with a built-in command
|
||||||
try:
|
try:
|
||||||
from hermes_cli.commands import resolve_command
|
from hermes_agent.cli.commands import resolve_command
|
||||||
if resolve_command(clean) is not None:
|
if resolve_command(clean) is not None:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Plugin '%s' tried to register command '/%s' which conflicts "
|
"Plugin '%s' tried to register command '/%s' which conflicts "
|
||||||
@@ -341,7 +341,7 @@ class PluginContext:
|
|||||||
Returns:
|
Returns:
|
||||||
JSON string from the tool handler (same format as model tool calls).
|
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).
|
# Wire up parent agent context when available (CLI mode).
|
||||||
# In gateway mode _cli_ref is None — tools degrade gracefully
|
# In gateway mode _cli_ref is None — tools degrade gracefully
|
||||||
@@ -372,7 +372,7 @@ class PluginContext:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
# Defer the import to avoid circular deps at module level
|
# 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):
|
if not isinstance(engine, ContextEngine):
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Plugin '%s' tried to register a context engine that does not "
|
"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``
|
``config.yaml`` matches against when routing ``image_generate``
|
||||||
tool calls.
|
tool calls.
|
||||||
"""
|
"""
|
||||||
from agent.image_gen_provider import ImageGenProvider
|
from hermes_agent.agent.image_gen.provider import ImageGenProvider
|
||||||
from agent.image_gen_registry import register_provider
|
from hermes_agent.agent.image_gen.registry import register_provider
|
||||||
|
|
||||||
if not isinstance(provider, ImageGenProvider):
|
if not isinstance(provider, ImageGenProvider):
|
||||||
logger.warning(
|
logger.warning(
|
||||||
@@ -452,7 +452,7 @@ class PluginContext:
|
|||||||
ValueError: if *name* contains ``':'`` or invalid characters.
|
ValueError: if *name* contains ``':'`` or invalid characters.
|
||||||
FileNotFoundError: if *path* does not exist.
|
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:
|
if ":" in name:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
@@ -1087,7 +1087,7 @@ def get_plugin_toolsets() -> List[tuple]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from tools.registry import registry
|
from hermes_agent.tools.registry import registry
|
||||||
except Exception:
|
except Exception:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_agent.constants import get_hermes_home
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -173,8 +173,8 @@ def _prompt_plugin_env_vars(manifest: dict, console) -> None:
|
|||||||
if not requires_env:
|
if not requires_env:
|
||||||
return
|
return
|
||||||
|
|
||||||
from hermes_cli.config import get_env_value, save_env_value # noqa: F811
|
from hermes_agent.cli.config import get_env_value, save_env_value # noqa: F811
|
||||||
from hermes_constants import display_hermes_home
|
from hermes_agent.constants import display_hermes_home
|
||||||
|
|
||||||
# Normalise to list-of-dicts
|
# Normalise to list-of-dicts
|
||||||
env_specs: list[dict] = []
|
env_specs: list[dict] = []
|
||||||
@@ -360,7 +360,7 @@ def cmd_install(
|
|||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if mv_int > _SUPPORTED_MANIFEST_VERSION:
|
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(
|
console.print(
|
||||||
f"[red]Error:[/red] Plugin '{plugin_name}' requires manifest_version "
|
f"[red]Error:[/red] Plugin '{plugin_name}' requires manifest_version "
|
||||||
f"{mv}, but this installer only supports up to {_SUPPORTED_MANIFEST_VERSION}.\n"
|
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``.
|
listed in ``plugins.enabled``.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
config = load_config()
|
config = load_config()
|
||||||
disabled = config.get("plugins", {}).get("disabled", [])
|
disabled = config.get("plugins", {}).get("disabled", [])
|
||||||
return set(disabled) if isinstance(disabled, list) else set()
|
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:
|
def _save_disabled_set(disabled: set) -> None:
|
||||||
"""Write the disabled plugins list to config.yaml."""
|
"""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()
|
config = load_config()
|
||||||
if "plugins" not in config:
|
if "plugins" not in config:
|
||||||
config["plugins"] = {}
|
config["plugins"] = {}
|
||||||
@@ -542,7 +542,7 @@ def _get_enabled_set() -> set:
|
|||||||
the key is missing (same behaviour as "nothing enabled yet").
|
the key is missing (same behaviour as "nothing enabled yet").
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
config = load_config()
|
config = load_config()
|
||||||
plugins_cfg = config.get("plugins", {})
|
plugins_cfg = config.get("plugins", {})
|
||||||
if not isinstance(plugins_cfg, dict):
|
if not isinstance(plugins_cfg, dict):
|
||||||
@@ -555,7 +555,7 @@ def _get_enabled_set() -> set:
|
|||||||
|
|
||||||
def _save_enabled_set(enabled: set) -> None:
|
def _save_enabled_set(enabled: set) -> None:
|
||||||
"""Write the enabled plugins list to config.yaml."""
|
"""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()
|
config = load_config()
|
||||||
if "plugins" not in config:
|
if "plugins" not in config:
|
||||||
config["plugins"] = {}
|
config["plugins"] = {}
|
||||||
@@ -631,8 +631,8 @@ def _plugin_exists(name: str) -> bool:
|
|||||||
return True
|
return True
|
||||||
# Bundled: <repo>/plugins/<name>/
|
# Bundled: <repo>/plugins/<name>/
|
||||||
from pathlib import Path as _P
|
from pathlib import Path as _P
|
||||||
import hermes_cli
|
import hermes_agent.cli as _cli_pkg
|
||||||
repo_plugins = _P(hermes_cli.__file__).resolve().parent.parent / "plugins"
|
repo_plugins = _P(_cli_pkg.__file__).resolve().parent.parent / "plugins"
|
||||||
if repo_plugins.is_dir():
|
if repo_plugins.is_dir():
|
||||||
candidate = repo_plugins / name
|
candidate = repo_plugins / name
|
||||||
if candidate.is_dir() and (
|
if candidate.is_dir() and (
|
||||||
@@ -659,8 +659,8 @@ def _discover_all_plugins() -> list:
|
|||||||
seen: dict = {} # name -> (name, version, description, source, path)
|
seen: dict = {} # name -> (name, version, description, source, path)
|
||||||
|
|
||||||
# Bundled (<repo>/plugins/<name>/), excluding memory/ and context_engine/
|
# Bundled (<repo>/plugins/<name>/), excluding memory/ and context_engine/
|
||||||
import hermes_cli
|
import hermes_agent.cli as _cli_pkg
|
||||||
repo_plugins = Path(hermes_cli.__file__).resolve().parent.parent / "plugins"
|
repo_plugins = Path(_cli_pkg.__file__).resolve().parent.parent / "plugins"
|
||||||
for base, source in ((repo_plugins, "bundled"), (_plugins_dir(), "user")):
|
for base, source in ((repo_plugins, "bundled"), (_plugins_dir(), "user")):
|
||||||
if not base.is_dir():
|
if not base.is_dir():
|
||||||
continue
|
continue
|
||||||
@@ -743,7 +743,7 @@ def cmd_list() -> None:
|
|||||||
def _discover_memory_providers() -> list[tuple[str, str]]:
|
def _discover_memory_providers() -> list[tuple[str, str]]:
|
||||||
"""Return [(name, description), ...] for available memory providers."""
|
"""Return [(name, description), ...] for available memory providers."""
|
||||||
try:
|
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()]
|
return [(name, desc) for name, desc, _avail in discover_memory_providers()]
|
||||||
except Exception:
|
except Exception:
|
||||||
return []
|
return []
|
||||||
@@ -752,7 +752,7 @@ def _discover_memory_providers() -> list[tuple[str, str]]:
|
|||||||
def _discover_context_engines() -> list[tuple[str, str]]:
|
def _discover_context_engines() -> list[tuple[str, str]]:
|
||||||
"""Return [(name, description), ...] for available context engines."""
|
"""Return [(name, description), ...] for available context engines."""
|
||||||
try:
|
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()]
|
return [(name, desc) for name, desc, _avail in discover_context_engines()]
|
||||||
except Exception:
|
except Exception:
|
||||||
return []
|
return []
|
||||||
@@ -761,7 +761,7 @@ def _discover_context_engines() -> list[tuple[str, str]]:
|
|||||||
def _get_current_memory_provider() -> str:
|
def _get_current_memory_provider() -> str:
|
||||||
"""Return the current memory.provider from config (empty = built-in)."""
|
"""Return the current memory.provider from config (empty = built-in)."""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
config = load_config()
|
config = load_config()
|
||||||
return config.get("memory", {}).get("provider", "") or ""
|
return config.get("memory", {}).get("provider", "") or ""
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -771,7 +771,7 @@ def _get_current_memory_provider() -> str:
|
|||||||
def _get_current_context_engine() -> str:
|
def _get_current_context_engine() -> str:
|
||||||
"""Return the current context.engine from config."""
|
"""Return the current context.engine from config."""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
config = load_config()
|
config = load_config()
|
||||||
return config.get("context", {}).get("engine", "compressor") or "compressor"
|
return config.get("context", {}).get("engine", "compressor") or "compressor"
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -780,7 +780,7 @@ def _get_current_context_engine() -> str:
|
|||||||
|
|
||||||
def _save_memory_provider(name: str) -> None:
|
def _save_memory_provider(name: str) -> None:
|
||||||
"""Persist memory.provider to config.yaml."""
|
"""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()
|
config = load_config()
|
||||||
if "memory" not in config:
|
if "memory" not in config:
|
||||||
config["memory"] = {}
|
config["memory"] = {}
|
||||||
@@ -790,7 +790,7 @@ def _save_memory_provider(name: str) -> None:
|
|||||||
|
|
||||||
def _save_context_engine(name: str) -> None:
|
def _save_context_engine(name: str) -> None:
|
||||||
"""Persist context.engine to config.yaml."""
|
"""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()
|
config = load_config()
|
||||||
if "context" not in config:
|
if "context" not in config:
|
||||||
config["context"] = {}
|
config["context"] = {}
|
||||||
@@ -800,7 +800,7 @@ def _save_context_engine(name: str) -> None:
|
|||||||
|
|
||||||
def _configure_memory_provider() -> bool:
|
def _configure_memory_provider() -> bool:
|
||||||
"""Launch a radio picker for memory providers. Returns True if changed."""
|
"""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()
|
current = _get_current_memory_provider()
|
||||||
providers = _discover_memory_providers()
|
providers = _discover_memory_providers()
|
||||||
@@ -838,7 +838,7 @@ def _configure_memory_provider() -> bool:
|
|||||||
|
|
||||||
def _configure_context_engine() -> bool:
|
def _configure_context_engine() -> bool:
|
||||||
"""Launch a radio picker for context engines. Returns True if changed."""
|
"""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()
|
current = _get_current_context_engine()
|
||||||
engines = _discover_context_engines()
|
engines = _discover_context_engines()
|
||||||
@@ -938,7 +938,7 @@ def cmd_toggle() -> None:
|
|||||||
def _run_composite_ui(curses, plugin_names, plugin_labels, plugin_selected,
|
def _run_composite_ui(curses, plugin_names, plugin_labels, plugin_selected,
|
||||||
disabled, categories, console):
|
disabled, categories, console):
|
||||||
"""Custom curses screen with checkboxes + category action rows."""
|
"""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)
|
chosen = set(plugin_selected)
|
||||||
n_plugins = len(plugin_names)
|
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,
|
def _run_composite_fallback(plugin_names, plugin_labels, plugin_selected,
|
||||||
disabled, categories, console):
|
disabled, categories, console):
|
||||||
"""Text-based fallback for the composite plugins UI."""
|
"""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))
|
print(color("\n Plugins", Colors.YELLOW))
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ _DEFAULT_EXPORT_EXCLUDE_ROOT = frozenset({
|
|||||||
"node_modules", # npm packages
|
"node_modules", # npm packages
|
||||||
# Databases & runtime state
|
# Databases & runtime state
|
||||||
"state.db", "state.db-shm", "state.db-wal",
|
"state.db", "state.db-shm", "state.db-wal",
|
||||||
"hermes_state.db",
|
"state.db",
|
||||||
"response_store.db", "response_store.db-shm", "response_store.db-wal",
|
"response_store.db", "response_store.db-shm", "response_store.db-wal",
|
||||||
"gateway.pid", "gateway_state.json", "processes.json",
|
"gateway.pid", "gateway_state.json", "processes.json",
|
||||||
"auth.json", # API keys, OAuth tokens, credential pools
|
"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``
|
In Docker/custom deployments where HERMES_HOME is outside ``~/.hermes``
|
||||||
(e.g. ``/opt/data``), returns HERMES_HOME directly.
|
(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()
|
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:
|
def _check_gateway_running(profile_dir: Path) -> bool:
|
||||||
"""Check if a gateway is running for a given profile directory."""
|
"""Check if a gateway is running for a given profile directory."""
|
||||||
try:
|
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
|
return get_running_pid(profile_dir / "gateway.pid", cleanup_stale=False) is not None
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
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 not None or clone_all or clone_config:
|
||||||
if clone_from is None:
|
if clone_from is None:
|
||||||
# Default: clone from active profile
|
# 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()
|
source_dir = get_hermes_home()
|
||||||
else:
|
else:
|
||||||
validate_profile_name(clone_from)
|
validate_profile_name(clone_from)
|
||||||
@@ -455,7 +455,7 @@ def create_profile(
|
|||||||
soul_path = profile_dir / "SOUL.md"
|
soul_path = profile_dir / "SOUL.md"
|
||||||
if not soul_path.exists():
|
if not soul_path.exists():
|
||||||
try:
|
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")
|
soul_path.write_text(DEFAULT_SOUL_MD, encoding="utf-8")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass # best-effort — don't fail profile creation over this
|
pass # best-effort — don't fail profile creation over this
|
||||||
@@ -469,11 +469,11 @@ def seed_profile_skills(profile_dir: Path, quiet: bool = False) -> Optional[dict
|
|||||||
Uses subprocess because sync_skills() caches HERMES_HOME at module level.
|
Uses subprocess because sync_skills() caches HERMES_HOME at module level.
|
||||||
Returns the sync result dict, or None on failure.
|
Returns the sync result dict, or None on failure.
|
||||||
"""
|
"""
|
||||||
project_root = Path(__file__).parent.parent.resolve()
|
project_root = Path(__file__).resolve().parents[2].resolve()
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
[sys.executable, "-c",
|
[sys.executable, "-c",
|
||||||
"import json; from tools.skills_sync import sync_skills; "
|
"import json; from hermes_agent.tools.skills.sync import sync_skills; "
|
||||||
"r = sync_skills(quiet=True); print(json.dumps(r))"],
|
"r = sync_skills(quiet=True); print(json.dumps(r))"],
|
||||||
env={**os.environ, "HERMES_HOME": str(profile_dir)},
|
env={**os.environ, "HERMES_HOME": str(profile_dir)},
|
||||||
cwd=str(project_root),
|
cwd=str(project_root),
|
||||||
@@ -597,7 +597,7 @@ def _cleanup_gateway_service(name: str, profile_dir: Path) -> None:
|
|||||||
old_home = os.environ.get("HERMES_HOME")
|
old_home = os.environ.get("HERMES_HOME")
|
||||||
try:
|
try:
|
||||||
os.environ["HERMES_HOME"] = str(profile_dir)
|
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":
|
if _platform.system() == "Linux":
|
||||||
svc_name = get_service_name()
|
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 the profile name if HERMES_HOME points into ``~/.hermes/profiles/<name>``.
|
||||||
Returns ``"custom"`` if HERMES_HOME is set to an unrecognized path.
|
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()
|
hermes_home = get_hermes_home()
|
||||||
resolved = hermes_home.resolve()
|
resolved = hermes_home.resolve()
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ import logging
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -341,7 +341,7 @@ def get_provider(name: str) -> Optional[ProviderDef]:
|
|||||||
|
|
||||||
# Try to get models.dev data
|
# Try to get models.dev data
|
||||||
try:
|
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)
|
mdev_info = _mdev_provider(canonical)
|
||||||
except Exception:
|
except Exception:
|
||||||
mdev_info = None
|
mdev_info = None
|
||||||
@@ -596,7 +596,7 @@ def resolve_provider_full(
|
|||||||
|
|
||||||
# 3. Try models.dev directly (for providers not in our ALIASES)
|
# 3. Try models.dev directly (for providers not in our ALIASES)
|
||||||
try:
|
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)
|
mdev_info = _mdev_provider(canonical)
|
||||||
if mdev_info is not None:
|
if mdev_info is not None:
|
||||||
return ProviderDef(
|
return ProviderDef(
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -9,9 +9,9 @@ from typing import Any, Dict, Optional
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
from hermes_cli import auth as auth_mod
|
from hermes_agent.cli.auth import auth as auth_mod
|
||||||
from agent.credential_pool import CredentialPool, PooledCredential, get_custom_provider_pool_key, load_pool
|
from hermes_agent.providers.credential_pool import CredentialPool, PooledCredential, get_custom_provider_pool_key, load_pool
|
||||||
from hermes_cli.auth import (
|
from hermes_agent.cli.auth.auth import (
|
||||||
AuthError,
|
AuthError,
|
||||||
DEFAULT_CODEX_BASE_URL,
|
DEFAULT_CODEX_BASE_URL,
|
||||||
DEFAULT_QWEN_BASE_URL,
|
DEFAULT_QWEN_BASE_URL,
|
||||||
@@ -27,9 +27,9 @@ from hermes_cli.auth import (
|
|||||||
resolve_external_process_provider_credentials,
|
resolve_external_process_provider_credentials,
|
||||||
has_usable_secret,
|
has_usable_secret,
|
||||||
)
|
)
|
||||||
from hermes_cli.config import get_compatible_custom_providers, load_config
|
from hermes_agent.cli.config import get_compatible_custom_providers, load_config
|
||||||
from hermes_constants import OPENROUTER_BASE_URL
|
from hermes_agent.constants import OPENROUTER_BASE_URL
|
||||||
from utils import base_url_host_matches, base_url_hostname
|
from hermes_agent.utils import base_url_host_matches, base_url_hostname
|
||||||
|
|
||||||
|
|
||||||
def _normalize_custom_provider_name(value: str) -> str:
|
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"
|
return "chat_completions"
|
||||||
|
|
||||||
try:
|
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)
|
return copilot_model_api_mode(model_name, api_key=api_key)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -206,7 +206,7 @@ def _resolve_runtime_from_pool_entry(
|
|||||||
if configured_mode and _provider_supports_explicit_api_mode(provider, configured_provider):
|
if configured_mode and _provider_supports_explicit_api_mode(provider, configured_provider):
|
||||||
api_mode = configured_mode
|
api_mode = configured_mode
|
||||||
elif provider in ("opencode-zen", "opencode-go"):
|
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", ""))
|
api_mode = opencode_model_api_mode(provider, model_cfg.get("default", ""))
|
||||||
else:
|
else:
|
||||||
# Auto-detect Anthropic-compatible endpoints (/anthropic suffix,
|
# 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"
|
base_url = explicit_base_url or cfg_base_url or "https://api.anthropic.com"
|
||||||
api_key = explicit_api_key
|
api_key = explicit_api_key
|
||||||
if not 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()
|
api_key = resolve_anthropic_token()
|
||||||
if not api_key:
|
if not api_key:
|
||||||
@@ -870,7 +870,7 @@ def resolve_runtime_provider(
|
|||||||
|
|
||||||
# Anthropic (native Messages API)
|
# Anthropic (native Messages API)
|
||||||
if provider == "anthropic":
|
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()
|
token = resolve_anthropic_token()
|
||||||
if not token:
|
if not token:
|
||||||
raise AuthError(
|
raise AuthError(
|
||||||
@@ -896,7 +896,7 @@ def resolve_runtime_provider(
|
|||||||
|
|
||||||
# AWS Bedrock (native Converse API via boto3)
|
# AWS Bedrock (native Converse API via boto3)
|
||||||
if provider == "bedrock":
|
if provider == "bedrock":
|
||||||
from agent.bedrock_adapter import (
|
from hermes_agent.providers.bedrock_adapter import (
|
||||||
has_aws_credentials,
|
has_aws_credentials,
|
||||||
resolve_aws_auth_env_var,
|
resolve_aws_auth_env_var,
|
||||||
resolve_bedrock_region,
|
resolve_bedrock_region,
|
||||||
@@ -989,7 +989,7 @@ def resolve_runtime_provider(
|
|||||||
if configured_mode and _provider_supports_explicit_api_mode(provider, configured_provider):
|
if configured_mode and _provider_supports_explicit_api_mode(provider, configured_provider):
|
||||||
api_mode = configured_mode
|
api_mode = configured_mode
|
||||||
elif provider in ("opencode-zen", "opencode-go"):
|
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", ""))
|
api_mode = opencode_model_api_mode(provider, model_cfg.get("default", ""))
|
||||||
else:
|
else:
|
||||||
# Auto-detect Anthropic-compatible endpoints by URL convention
|
# Auto-detect Anthropic-compatible endpoints by URL convention
|
||||||
@@ -20,14 +20,14 @@ import copy
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Dict, Any
|
from typing import Optional, Dict, Any
|
||||||
|
|
||||||
from hermes_cli.nous_subscription import get_nous_subscription_features
|
from hermes_agent.cli.nous_subscription import get_nous_subscription_features
|
||||||
from tools.tool_backend_helpers import managed_nous_tools_enabled
|
from hermes_agent.tools.backend_helpers import managed_nous_tools_enabled
|
||||||
from utils import base_url_hostname
|
from hermes_agent.utils import base_url_hostname
|
||||||
from hermes_constants import get_optional_skills_dir
|
from hermes_agent.constants import get_optional_skills_dir
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
PROJECT_ROOT = Path(__file__).parent.parent.resolve()
|
PROJECT_ROOT = Path(__file__).resolve().parents[2].resolve()
|
||||||
|
|
||||||
_DOCS_BASE = "https://hermes-agent.nousresearch.com/docs"
|
_DOCS_BASE = "https://hermes-agent.nousresearch.com/docs"
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ def _supports_same_provider_pool_setup(provider: str) -> bool:
|
|||||||
return False
|
return False
|
||||||
if provider == "openrouter":
|
if provider == "openrouter":
|
||||||
return True
|
return True
|
||||||
from hermes_cli.auth import PROVIDER_REGISTRY
|
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
|
||||||
|
|
||||||
pconfig = PROVIDER_REGISTRY.get(provider)
|
pconfig = PROVIDER_REGISTRY.get(provider)
|
||||||
if not pconfig:
|
if not pconfig:
|
||||||
@@ -129,7 +129,7 @@ def _set_reasoning_effort(config: Dict[str, Any], effort: str) -> None:
|
|||||||
|
|
||||||
|
|
||||||
# Import config helpers
|
# Import config helpers
|
||||||
from hermes_cli.config import (
|
from hermes_agent.cli.config import (
|
||||||
DEFAULT_CONFIG,
|
DEFAULT_CONFIG,
|
||||||
get_hermes_home,
|
get_hermes_home,
|
||||||
get_config_path,
|
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)
|
# 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):
|
def print_header(title: str):
|
||||||
@@ -151,7 +151,7 @@ def print_header(title: str):
|
|||||||
print(color(f"◆ {title}", Colors.CYAN, Colors.BOLD))
|
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_error,
|
||||||
print_info,
|
print_info,
|
||||||
print_success,
|
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:
|
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."""
|
"""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)
|
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:
|
if pre_selected is None:
|
||||||
pre_selected = []
|
pre_selected = []
|
||||||
|
|
||||||
from hermes_cli.curses_ui import curses_checklist
|
from hermes_agent.cli.ui.curses import curses_checklist
|
||||||
|
|
||||||
chosen = curses_checklist(
|
chosen = curses_checklist(
|
||||||
title,
|
title,
|
||||||
@@ -352,7 +352,7 @@ def _print_setup_summary(config: dict, hermes_home):
|
|||||||
|
|
||||||
# Vision — use the same runtime resolver as the actual vision tools
|
# Vision — use the same runtime resolver as the actual vision tools
|
||||||
try:
|
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()
|
_vision_backends = get_available_vision_backends()
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -419,8 +419,8 @@ def _print_setup_summary(config: dict, hermes_home):
|
|||||||
# setups don't show as "missing FAL_KEY".
|
# setups don't show as "missing FAL_KEY".
|
||||||
_img_backend = None
|
_img_backend = None
|
||||||
try:
|
try:
|
||||||
from agent.image_gen_registry import list_providers
|
from hermes_agent.agent.image_gen.registry import list_providers
|
||||||
from hermes_cli.plugins import _ensure_plugins_discovered
|
from hermes_agent.cli.plugins import _ensure_plugins_discovered
|
||||||
|
|
||||||
_ensure_plugins_discovered()
|
_ensure_plugins_discovered()
|
||||||
for _p in list_providers():
|
for _p in list_providers():
|
||||||
@@ -536,7 +536,7 @@ def _print_setup_summary(config: dict, hermes_home):
|
|||||||
print_warning(
|
print_warning(
|
||||||
"Some tools are disabled. Run 'hermes setup tools' to configure them,"
|
"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_warning(f"or edit {_dhh()}/.env directly to add the missing API keys.")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
@@ -560,7 +560,7 @@ def _print_setup_summary(config: dict, hermes_home):
|
|||||||
print()
|
print()
|
||||||
|
|
||||||
# Show file locations prominently
|
# 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(color(f"📁 All your files are in {_dhh()}/:", Colors.CYAN, Colors.BOLD))
|
||||||
print()
|
print()
|
||||||
print(f" {color('Settings:', Colors.YELLOW)} {get_config_path()}")
|
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
|
When *quick* is True, skips credential rotation, vision, and TTS
|
||||||
configuration — used by the streamlined first-time quick setup.
|
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_header("Inference Provider")
|
||||||
print_info("Choose how to connect to your main chat model.")
|
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,
|
# Delegate to the shared hermes model flow — handles provider picker,
|
||||||
# credential prompting, model selection, and config persistence.
|
# 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:
|
try:
|
||||||
select_provider_and_model()
|
select_provider_and_model()
|
||||||
except (SystemExit, KeyboardInterrupt):
|
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):
|
if not quick and _supports_same_provider_pool_setup(selected_provider):
|
||||||
try:
|
try:
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from agent.credential_pool import load_pool
|
from hermes_agent.providers.credential_pool import load_pool
|
||||||
from hermes_cli.auth_commands import auth_add_command
|
from hermes_agent.cli.auth.commands import auth_add_command
|
||||||
|
|
||||||
pool = load_pool(selected_provider)
|
pool = load_pool(selected_provider)
|
||||||
entries = pool.entries()
|
entries = pool.entries()
|
||||||
@@ -786,7 +786,7 @@ def setup_model_provider(config: dict, *, quick: bool = False):
|
|||||||
_vision_needs_setup = False
|
_vision_needs_setup = False
|
||||||
else:
|
else:
|
||||||
try:
|
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())
|
_vision_backends = set(get_available_vision_backends())
|
||||||
except Exception:
|
except Exception:
|
||||||
_vision_backends = set()
|
_vision_backends = set()
|
||||||
@@ -1075,7 +1075,7 @@ def _setup_tts_provider(config: dict):
|
|||||||
save_env_value("XAI_API_KEY", api_key)
|
save_env_value("XAI_API_KEY", api_key)
|
||||||
print_success("xAI TTS API key saved")
|
print_success("xAI TTS API key saved")
|
||||||
else:
|
else:
|
||||||
from hermes_constants import display_hermes_home as _dhh
|
from hermes_agent.constants import display_hermes_home as _dhh
|
||||||
print_warning(
|
print_warning(
|
||||||
"No xAI API key provided for TTS. Configure XAI_API_KEY via "
|
"No xAI API key provided for TTS. Configure XAI_API_KEY via "
|
||||||
f"hermes setup model or {_dhh()}/.env to use xAI TTS. "
|
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":
|
elif selected_backend == "modal":
|
||||||
print_success("Terminal backend: Modal")
|
print_success("Terminal backend: Modal")
|
||||||
print_info("Serverless cloud sandboxes. Each session gets its own container.")
|
print_info("Serverless cloud sandboxes. Each session gets its own container.")
|
||||||
from tools.managed_tool_gateway import is_managed_tool_gateway_ready
|
from hermes_agent.tools.managed_gateway import is_managed_tool_gateway_ready
|
||||||
from tools.tool_backend_helpers import normalize_modal_mode
|
from hermes_agent.tools.backend_helpers import normalize_modal_mode
|
||||||
|
|
||||||
managed_modal_available = bool(
|
managed_modal_available = bool(
|
||||||
managed_nous_tools_enabled()
|
managed_nous_tools_enabled()
|
||||||
@@ -2040,49 +2040,49 @@ def _setup_whatsapp():
|
|||||||
|
|
||||||
def _setup_weixin():
|
def _setup_weixin():
|
||||||
"""Configure Weixin (personal WeChat) via iLink Bot API QR login."""
|
"""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()
|
_gateway_setup_weixin()
|
||||||
|
|
||||||
|
|
||||||
def _setup_signal():
|
def _setup_signal():
|
||||||
"""Configure Signal via gateway setup."""
|
"""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()
|
_gateway_setup_signal()
|
||||||
|
|
||||||
|
|
||||||
def _setup_email():
|
def _setup_email():
|
||||||
"""Configure Email via gateway setup."""
|
"""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()
|
_gateway_setup_email()
|
||||||
|
|
||||||
|
|
||||||
def _setup_sms():
|
def _setup_sms():
|
||||||
"""Configure SMS (Twilio) via gateway setup."""
|
"""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()
|
_gateway_setup_sms()
|
||||||
|
|
||||||
|
|
||||||
def _setup_dingtalk():
|
def _setup_dingtalk():
|
||||||
"""Configure DingTalk via gateway setup."""
|
"""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()
|
_gateway_setup_dingtalk()
|
||||||
|
|
||||||
|
|
||||||
def _setup_feishu():
|
def _setup_feishu():
|
||||||
"""Configure Feishu / Lark via gateway setup."""
|
"""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()
|
_gateway_setup_feishu()
|
||||||
|
|
||||||
|
|
||||||
def _setup_wecom():
|
def _setup_wecom():
|
||||||
"""Configure WeCom (Enterprise WeChat) via gateway setup."""
|
"""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()
|
_gateway_setup_wecom()
|
||||||
|
|
||||||
|
|
||||||
def _setup_wecom_callback():
|
def _setup_wecom_callback():
|
||||||
"""Configure WeCom Callback (self-built app) via gateway setup."""
|
"""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()
|
_gw_setup()
|
||||||
|
|
||||||
|
|
||||||
@@ -2155,7 +2155,7 @@ def _setup_bluebubbles():
|
|||||||
|
|
||||||
def _setup_qqbot():
|
def _setup_qqbot():
|
||||||
"""Configure QQ Bot (Official API v2) via gateway setup."""
|
"""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()
|
_gateway_setup_qqbot()
|
||||||
|
|
||||||
|
|
||||||
@@ -2194,7 +2194,7 @@ def _setup_webhooks():
|
|||||||
save_env_value("WEBHOOK_ENABLED", "true")
|
save_env_value("WEBHOOK_ENABLED", "true")
|
||||||
print()
|
print()
|
||||||
print_success("Webhooks enabled! Next steps:")
|
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(f" 1. Define webhook routes in {_dhh()}/config.yaml")
|
||||||
print_info(" 2. Point your service (GitHub, GitLab, etc.) at:")
|
print_info(" 2. Point your service (GitHub, GitLab, etc.) at:")
|
||||||
print_info(" http://your-server:8644/webhooks/<route-name>")
|
print_info(" http://your-server:8644/webhooks/<route-name>")
|
||||||
@@ -2318,7 +2318,7 @@ def setup_gateway(config: dict):
|
|||||||
_is_linux = _platform.system() == "Linux"
|
_is_linux = _platform.system() == "Linux"
|
||||||
_is_macos = _platform.system() == "Darwin"
|
_is_macos = _platform.system() == "Darwin"
|
||||||
|
|
||||||
from hermes_cli.gateway import (
|
from hermes_agent.cli.gateway import (
|
||||||
_is_service_installed,
|
_is_service_installed,
|
||||||
_is_service_running,
|
_is_service_running,
|
||||||
supports_systemd_services,
|
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 as a boot-time service: sudo hermes gateway install --system")
|
||||||
print_info(" Or run in foreground: hermes gateway")
|
print_info(" Or run in foreground: hermes gateway")
|
||||||
else:
|
else:
|
||||||
from hermes_constants import is_container
|
from hermes_agent.constants import is_container
|
||||||
if is_container():
|
if is_container():
|
||||||
print_info("Start the gateway to bring your bots online:")
|
print_info("Start the gateway to bring your bots online:")
|
||||||
print_info(" hermes gateway run # Run as container main process")
|
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
|
first_install: When True, uses the simplified first-install flow
|
||||||
(no platform menu, prompts for all unconfigured API keys).
|
(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)
|
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.
|
``OPENAI_API_KEY`` / ``OPENROUTER_API_KEY`` values through OpenRouter.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from hermes_cli.auth import get_active_provider
|
from hermes_agent.cli.auth.auth import get_active_provider
|
||||||
if get_active_provider():
|
if get_active_provider():
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from hermes_cli.auth import PROVIDER_REGISTRY
|
from hermes_agent.cli.auth.auth import PROVIDER_REGISTRY
|
||||||
except Exception:
|
except Exception:
|
||||||
PROVIDER_REGISTRY = {} # type: ignore[assignment]
|
PROVIDER_REGISTRY = {} # type: ignore[assignment]
|
||||||
|
|
||||||
@@ -2863,7 +2863,7 @@ def run_setup_wizard(args):
|
|||||||
hermes setup tools — just tool configuration
|
hermes setup tools — just tool configuration
|
||||||
hermes setup agent — just agent settings
|
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():
|
if is_managed():
|
||||||
managed_error("run setup wizard")
|
managed_error("run setup wizard")
|
||||||
return
|
return
|
||||||
@@ -2918,7 +2918,7 @@ def run_setup_wizard(args):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Check if this is an existing installation with a provider configured
|
# 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()
|
active_provider = get_active_provider()
|
||||||
is_existing = (
|
is_existing = (
|
||||||
@@ -3072,8 +3072,8 @@ def _resolve_hermes_chat_argv() -> Optional[list[str]]:
|
|||||||
return [hermes_bin, "chat"]
|
return [hermes_bin, "chat"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if importlib.util.find_spec("hermes_cli") is not None:
|
if importlib.util.find_spec("hermes_agent.cli") is not None:
|
||||||
return [sys.executable, "-m", "hermes_cli.main", "chat"]
|
return [sys.executable, "-m", "hermes_agent.cli.main", "chat"]
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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):
|
def _run_quick_setup(config: dict, hermes_home):
|
||||||
"""Quick setup — only configure items that are missing."""
|
"""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_env_vars,
|
||||||
get_missing_config_fields,
|
get_missing_config_fields,
|
||||||
check_config_version,
|
check_config_version,
|
||||||
@@ -13,9 +13,9 @@ Config stored in ~/.hermes/config.yaml under:
|
|||||||
"""
|
"""
|
||||||
from typing import List, Optional, Set
|
from typing import List, Optional, Set
|
||||||
|
|
||||||
from hermes_cli.config import load_config, save_config
|
from hermes_agent.cli.config import load_config, save_config
|
||||||
from hermes_cli.colors import Colors, color
|
from hermes_agent.cli.ui.colors import Colors, color
|
||||||
from hermes_cli.platforms import PLATFORMS as _PLATFORMS
|
from hermes_agent.cli.platforms import PLATFORMS as _PLATFORMS
|
||||||
|
|
||||||
# Backward-compatible view: {key: label_string} so existing code that
|
# Backward-compatible view: {key: label_string} so existing code that
|
||||||
# iterates ``PLATFORMS.items()`` or calls ``PLATFORMS.get(key)`` keeps
|
# 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]:
|
def _list_all_skills() -> List[dict]:
|
||||||
"""Return all installed skills (ignoring disabled state)."""
|
"""Return all installed skills (ignoring disabled state)."""
|
||||||
try:
|
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)
|
return _find_all_skills(skip_disabled=True)
|
||||||
except Exception:
|
except Exception:
|
||||||
return []
|
return []
|
||||||
@@ -93,7 +93,7 @@ def _select_platform() -> Optional[str]:
|
|||||||
|
|
||||||
def _toggle_by_category(skills: List[dict], disabled: Set[str]) -> Set[str]:
|
def _toggle_by_category(skills: List[dict], disabled: Set[str]) -> Set[str]:
|
||||||
"""Toggle all skills in a category at once."""
|
"""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)
|
categories = _get_categories(skills)
|
||||||
cat_labels = []
|
cat_labels = []
|
||||||
@@ -124,7 +124,7 @@ def _toggle_by_category(skills: List[dict], disabled: Set[str]) -> Set[str]:
|
|||||||
|
|
||||||
def skills_command(args=None):
|
def skills_command(args=None):
|
||||||
"""Entry point for `hermes skills`."""
|
"""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()
|
config = load_config()
|
||||||
skills = _list_all_skills()
|
skills = _list_all_skills()
|
||||||
@@ -21,7 +21,7 @@ from rich.table import Table
|
|||||||
|
|
||||||
# Lazy imports to avoid circular dependencies and slow startup.
|
# Lazy imports to avoid circular dependencies and slow startup.
|
||||||
# tools.skills_hub and tools.skills_guard are imported inside functions.
|
# 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()
|
_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.
|
matches exist, shows them and asks the user to use the full identifier.
|
||||||
Returns empty string if nothing found or ambiguous.
|
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 = console or _console
|
||||||
c.print(f"[dim]Resolving '{name}'...[/]")
|
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,
|
def do_search(query: str, source: str = "all", limit: int = 10,
|
||||||
console: Optional[Console] = None) -> None:
|
console: Optional[Console] = None) -> None:
|
||||||
"""Search registries and display results as a Rich table."""
|
"""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 = console or _console
|
||||||
c.print(f"\n[bold]Searching for:[/] {query}")
|
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.
|
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,
|
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,
|
console: Optional[Console] = None, skip_confirm: bool = False,
|
||||||
invalidate_cache: bool = True) -> None:
|
invalidate_cache: bool = True) -> None:
|
||||||
"""Fetch, quarantine, scan, confirm, and install a skill."""
|
"""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,
|
GitHubAuth, create_source_router, ensure_hub_dirs,
|
||||||
quarantine_bundle, install_from_quarantine, HubLockFile,
|
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
|
c = console or _console
|
||||||
ensure_hub_dirs()
|
ensure_hub_dirs()
|
||||||
@@ -377,7 +377,7 @@ def do_install(identifier: str, category: str = "", force: bool = False,
|
|||||||
q_path = quarantine_bundle(bundle)
|
q_path = quarantine_bundle(bundle)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
c.print(f"[bold red]Installation blocked:[/] {exc}\n")
|
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,
|
append_audit_log("BLOCKED", bundle.name, bundle.source,
|
||||||
bundle.trust_level, "invalid_path", str(exc))
|
bundle.trust_level, "invalid_path", str(exc))
|
||||||
return
|
return
|
||||||
@@ -395,7 +395,7 @@ def do_install(identifier: str, category: str = "", force: bool = False,
|
|||||||
c.print(f"\n[bold red]Installation blocked:[/] {reason}")
|
c.print(f"\n[bold red]Installation blocked:[/] {reason}")
|
||||||
# Clean up quarantine
|
# Clean up quarantine
|
||||||
shutil.rmtree(q_path, ignore_errors=True)
|
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,
|
append_audit_log("BLOCKED", bundle.name, bundle.source,
|
||||||
bundle.trust_level, result.verdict,
|
bundle.trust_level, result.verdict,
|
||||||
f"{len(result.findings)}_findings")
|
f"{len(result.findings)}_findings")
|
||||||
@@ -445,18 +445,18 @@ def do_install(identifier: str, category: str = "", force: bool = False,
|
|||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
c.print(f"[bold red]Installation blocked:[/] {exc}\n")
|
c.print(f"[bold red]Installation blocked:[/] {exc}\n")
|
||||||
shutil.rmtree(q_path, ignore_errors=True)
|
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,
|
append_audit_log("BLOCKED", bundle.name, bundle.source,
|
||||||
bundle.trust_level, "invalid_path", str(exc))
|
bundle.trust_level, "invalid_path", str(exc))
|
||||||
return
|
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"[bold green]Installed:[/] {install_dir.relative_to(SKILLS_DIR)}")
|
||||||
c.print(f"[dim]Files: {', '.join(bundle.files.keys())}[/]\n")
|
c.print(f"[dim]Files: {', '.join(bundle.files.keys())}[/]\n")
|
||||||
|
|
||||||
if invalidate_cache:
|
if invalidate_cache:
|
||||||
# Invalidate the skills prompt cache so the new skill appears immediately
|
# Invalidate the skills prompt cache so the new skill appears immediately
|
||||||
try:
|
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)
|
clear_skills_system_prompt_cache(clear_snapshot=True)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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:
|
def do_inspect(identifier: str, console: Optional[Console] = None) -> None:
|
||||||
"""Preview a skill's SKILL.md content without installing."""
|
"""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
|
c = console or _console
|
||||||
auth = GitHubAuth()
|
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}``.
|
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))
|
page_size = max(1, min(page_size, 100))
|
||||||
_TRUST_RANK = {"builtin": 3, "trusted": 2, "community": 1}
|
_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]:
|
def inspect_skill(identifier: str) -> Optional[dict]:
|
||||||
"""Skill metadata (+ SKILL.md preview) for programmatic callers."""
|
"""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:
|
class _Q:
|
||||||
def print(self, *a, **k):
|
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:
|
def do_list(source_filter: str = "all", console: Optional[Console] = None) -> None:
|
||||||
"""List installed skills, distinguishing hub, builtin, and local skills."""
|
"""List installed skills, distinguishing hub, builtin, and local skills."""
|
||||||
from tools.skills_hub import HubLockFile, ensure_hub_dirs
|
from hermes_agent.tools.skills.hub import HubLockFile, ensure_hub_dirs
|
||||||
from tools.skills_sync import _read_manifest
|
from hermes_agent.tools.skills.sync import _read_manifest
|
||||||
from tools.skills_tool import _find_all_skills
|
from hermes_agent.tools.skills.tool import _find_all_skills
|
||||||
|
|
||||||
c = console or _console
|
c = console or _console
|
||||||
ensure_hub_dirs()
|
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:
|
def do_check(name: Optional[str] = None, console: Optional[Console] = None) -> None:
|
||||||
"""Check hub-installed skills for upstream updates."""
|
"""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
|
c = console or _console
|
||||||
results = check_for_skill_updates(name=name)
|
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:
|
def do_update(name: Optional[str] = None, console: Optional[Console] = None) -> None:
|
||||||
"""Update hub-installed skills with upstream changes."""
|
"""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
|
c = console or _console
|
||||||
lock = HubLockFile()
|
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:
|
def do_audit(name: Optional[str] = None, console: Optional[Console] = None) -> None:
|
||||||
"""Re-run security scan on installed hub skills."""
|
"""Re-run security scan on installed hub skills."""
|
||||||
from tools.skills_hub import HubLockFile, SKILLS_DIR
|
from hermes_agent.tools.skills.hub import HubLockFile, SKILLS_DIR
|
||||||
from tools.skills_guard import scan_skill, format_scan_report
|
from hermes_agent.tools.skills.guard import scan_skill, format_scan_report
|
||||||
|
|
||||||
c = console or _console
|
c = console or _console
|
||||||
lock = HubLockFile()
|
lock = HubLockFile()
|
||||||
@@ -737,7 +737,7 @@ def do_uninstall(name: str, console: Optional[Console] = None,
|
|||||||
skip_confirm: bool = False,
|
skip_confirm: bool = False,
|
||||||
invalidate_cache: bool = True) -> None:
|
invalidate_cache: bool = True) -> None:
|
||||||
"""Remove a hub-installed skill with confirmation."""
|
"""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
|
c = console or _console
|
||||||
|
|
||||||
@@ -757,7 +757,7 @@ def do_uninstall(name: str, console: Optional[Console] = None,
|
|||||||
c.print(f"[bold green]{msg}[/]\n")
|
c.print(f"[bold green]{msg}[/]\n")
|
||||||
if invalidate_cache:
|
if invalidate_cache:
|
||||||
try:
|
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)
|
clear_skills_system_prompt_cache(clear_snapshot=True)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -773,7 +773,7 @@ def do_reset(name: str, restore: bool = False,
|
|||||||
skip_confirm: bool = False,
|
skip_confirm: bool = False,
|
||||||
invalidate_cache: bool = True) -> None:
|
invalidate_cache: bool = True) -> None:
|
||||||
"""Reset a bundled skill's manifest tracking (+ optionally restore from bundled)."""
|
"""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
|
c = console or _console
|
||||||
|
|
||||||
@@ -804,7 +804,7 @@ def do_reset(name: str, restore: bool = False,
|
|||||||
|
|
||||||
if invalidate_cache:
|
if invalidate_cache:
|
||||||
try:
|
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)
|
clear_skills_system_prompt_cache(clear_snapshot=True)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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:
|
def do_tap(action: str, repo: str = "", console: Optional[Console] = None) -> None:
|
||||||
"""Manage taps (custom GitHub repo sources)."""
|
"""Manage taps (custom GitHub repo sources)."""
|
||||||
from tools.skills_hub import TapsManager
|
from hermes_agent.tools.skills.hub import TapsManager
|
||||||
|
|
||||||
c = console or _console
|
c = console or _console
|
||||||
mgr = TapsManager()
|
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 = "",
|
def do_publish(skill_path: str, target: str = "github", repo: str = "",
|
||||||
console: Optional[Console] = None) -> None:
|
console: Optional[Console] = None) -> None:
|
||||||
"""Publish a local skill to a registry (GitHub PR or ClawHub submission)."""
|
"""Publish a local skill to a registry (GitHub PR or ClawHub submission)."""
|
||||||
from tools.skills_hub import GitHubAuth, SKILLS_DIR
|
from hermes_agent.tools.skills.hub import GitHubAuth, SKILLS_DIR
|
||||||
from tools.skills_guard import scan_skill, format_scan_report
|
from hermes_agent.tools.skills.guard import scan_skill, format_scan_report
|
||||||
|
|
||||||
c = console or _console
|
c = console or _console
|
||||||
path = Path(skill_path)
|
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:
|
def do_snapshot_export(output_path: str, console: Optional[Console] = None) -> None:
|
||||||
"""Export current hub skill configuration to a portable JSON file."""
|
"""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
|
c = console or _console
|
||||||
lock = HubLockFile()
|
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,
|
def do_snapshot_import(input_path: str, force: bool = False,
|
||||||
console: Optional[Console] = None) -> None:
|
console: Optional[Console] = None) -> None:
|
||||||
"""Re-install skills from a snapshot file."""
|
"""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
|
c = console or _console
|
||||||
inp = Path(input_path)
|
inp = Path(input_path)
|
||||||
@@ -19,7 +19,7 @@ def get_provider_request_timeout(
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ def get_provider_stale_timeout(
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_agent.cli.config import load_config
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user