mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
refactor(restructure): update infrastructure for hermes_agent package
Update pyproject.toml entry points, packages.find, and package-data. Delete py-modules (all top-level modules moved into hermes_agent/). Add hermes-skills-sync console_script entry point. Update Dockerfile HERMES_WEB_DIST path. Update docker/entrypoint.sh, scripts/install.sh, setup-hermes.sh to use hermes-skills-sync console_script. Update web/vite.config.ts output directory. Update MANIFEST.in to graft hermes_agent. Update AGENTS.md project structure to reflect new layout. Part of #14182, #14183
This commit is contained in:
parent
a1e667b9f2
commit
76aebd73c3
8 changed files with 78 additions and 88 deletions
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__
|
||||||
|
|
|
||||||
|
|
@ -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 "$@"
|
||||||
|
|
|
||||||
|
|
@ -117,18 +117,16 @@ all = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
hermes = "hermes_cli.main:main"
|
hermes = "hermes_agent.cli.main:main"
|
||||||
hermes-agent = "run_agent:main"
|
hermes-agent = "hermes_agent.agent.loop:main"
|
||||||
hermes-acp = "acp_adapter.entry:main"
|
hermes-acp = "hermes_agent.acp.entry:main"
|
||||||
|
hermes-skills-sync = "hermes_agent.tools.skills.sync:main"
|
||||||
[tool.setuptools]
|
|
||||||
py-modules = ["run_agent", "model_tools", "toolsets", "toolset_distributions", "cli", "hermes_constants", "hermes_state", "hermes_time", "hermes_logging", "utils"]
|
|
||||||
|
|
||||||
[tool.setuptools.package-data]
|
[tool.setuptools.package-data]
|
||||||
hermes_cli = ["web_dist/**/*"]
|
hermes_agent = ["cli/web_dist/**/*"]
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
include = ["agent", "tools", "tools.*", "hermes_cli", "gateway", "gateway.*", "tui_gateway", "tui_gateway.*", "cron", "acp_adapter", "plugins", "plugins.*", "scripts"]
|
include = ["hermes_agent", "hermes_agent.*", "tui_gateway", "tui_gateway.*"]
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
testpaths = ["tests"]
|
testpaths = ["tests"]
|
||||||
|
|
|
||||||
|
|
@ -1102,7 +1102,7 @@ SOUL_EOF
|
||||||
|
|
||||||
# Seed bundled skills into ~/.hermes/skills/ (manifest-based, one-time per skill)
|
# Seed bundled skills into ~/.hermes/skills/ (manifest-based, one-time per skill)
|
||||||
log_info "Syncing bundled skills to ~/.hermes/skills/ ..."
|
log_info "Syncing bundled skills to ~/.hermes/skills/ ..."
|
||||||
if "$INSTALL_DIR/venv/bin/python" "$INSTALL_DIR/tools/skills_sync.py" 2>/dev/null; then
|
if "$INSTALL_DIR/venv/bin/hermes-skills-sync" 2>/dev/null; then
|
||||||
log_success "Skills synced to ~/.hermes/skills/"
|
log_success "Skills synced to ~/.hermes/skills/"
|
||||||
else
|
else
|
||||||
# Fallback: simple directory copy if Python sync fails
|
# Fallback: simple directory copy if Python sync fails
|
||||||
|
|
|
||||||
|
|
@ -341,7 +341,7 @@ mkdir -p "$HERMES_SKILLS_DIR"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Syncing bundled skills to ~/.hermes/skills/ ..."
|
echo "Syncing bundled skills to ~/.hermes/skills/ ..."
|
||||||
if "$SCRIPT_DIR/venv/bin/python" "$SCRIPT_DIR/tools/skills_sync.py" 2>/dev/null; then
|
if "$SCRIPT_DIR/venv/bin/hermes-skills-sync" 2>/dev/null; then
|
||||||
echo -e "${GREEN}✓${NC} Skills synced"
|
echo -e "${GREEN}✓${NC} Skills synced"
|
||||||
else
|
else
|
||||||
# Fallback: copy if sync script fails (missing deps, etc.)
|
# Fallback: copy if sync script fails (missing deps, etc.)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ const BACKEND = process.env.HERMES_DASHBOARD_URL ?? "http://127.0.0.1:9119";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In production the Python `hermes dashboard` server injects a one-shot
|
* In production the Python `hermes dashboard` server injects a one-shot
|
||||||
* session token into `index.html` (see `hermes_cli/web_server.py`). The
|
* session token into `index.html` (see `hermes_agent/cli/web_server.py`). The
|
||||||
* Vite dev server serves its own `index.html`, so unless we forward that
|
* Vite dev server serves its own `index.html`, so unless we forward that
|
||||||
* token, every protected `/api/*` call 401s.
|
* token, every protected `/api/*` call 401s.
|
||||||
*
|
*
|
||||||
|
|
@ -59,7 +59,7 @@ export default defineConfig({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
outDir: "../hermes_cli/web_dist",
|
outDir: "../hermes_agent/cli/web_dist",
|
||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue