Compare commits

..

1 Commits

Author SHA1 Message Date
Alexander Whitestone
2309e5c89c docs: add hermes-agent genome analysis (#668)
Some checks failed
Self-Healing Smoke / self-healing-smoke (pull_request) Failing after 19s
Smoke Test / smoke (pull_request) Failing after 19s
Agent PR Gate / gate (pull_request) Failing after 32s
Agent PR Gate / report (pull_request) Has been cancelled
2026-04-18 15:09:33 -04:00
4 changed files with 401 additions and 47 deletions

387
hermes-agent-GENOME.md Normal file
View File

@@ -0,0 +1,387 @@
# GENOME.md — hermes-agent
Generated from target repo `Timmy_Foundation/hermes-agent` at commit `05f8c2d1`.
This host-repo artifact lives in `timmy-home` so the codebase-genome backlog can track a repo-grounded analysis without depending on a mutable local checkout path.
## Project Overview
`hermes-agent` is the core agent framework that Timmy and the wider Nous ecosystem are hosted on top of. It is a multi-surface agent runtime with a synchronous tool-calling loop, a terminal UI, a cross-platform gateway, a cron scheduler, an ACP server for IDE/editor integration, a tool registry, and a very large automated test suite.
Grounded facts from the analyzed checkout:
- target repo path analyzed: `/Users/apayne/hermes-agent`
- target repo origin: `https://forge.alexanderwhitestone.com/Timmy_Foundation/hermes-agent.git`
- analyzed commit: `05f8c2d1`
- no `GENOME.md` exists on target `main`
- text files in the checkout: `3207`
- Python LOC from raw `find ... '*.py' | xargs wc -l`: `409,718`
- test collection command run: `python3 -m pytest tests --collect-only -q`
- collect-only result: `11850/11900 tests collected (50 deselected), 7 errors in 18.84s`
What the repo actually is:
1. a core synchronous agent runtime centered on `run_agent.py`
2. a configurable CLI/TUI product centered on `cli.py` and `hermes_cli/`
3. a tool platform centered on `tools/registry.py` and one-file-per-tool modules
4. a multi-platform messaging gateway centered on `gateway/run.py`
5. an editor/IDE integration surface under `acp_adapter/`
6. a scheduled automation layer under `cron/`
7. a research / RL / batch-generation surface (`batch_runner.py`, `environments/`, RL tooling)
The repo is not just “an agent.” It is an operating system for tool-using agents, with multiple runtime surfaces sharing one conversation loop, one session store, one config system, and one tool registry.
## Architecture Diagram
```mermaid
flowchart TD
user[User / Operator]
cli[CLI + TUI\ncli.py\nhermes_cli/main.py]
gateway[Messaging Gateway\ngateway/run.py\nplatform adapters]
acp[ACP / IDE Server\nacp_adapter/server.py]
cron[Cron Scheduler\ncron/scheduler.py]
batch[Batch + RL\nbatch_runner.py\nenvironments/]
agent[AIAgent Loop\nrun_agent.py]
model_tools[Tool / Model Orchestration\nmodel_tools.py\ntoolsets.py]
prompt[Prompt + Memory Layer\nagent/prompt_builder.py\nagent/context_compressor.py\nagent/prompt_caching.py]
tools[Tool Registry + Implementations\ntools/registry.py\nfile_tools.py\nmcp_tool.py\napproval.py]
state[Session / Persistence\nhermes_state.py\nSQLite + FTS5 + WAL mode]
config[Config + Setup\nhermes_cli/config.py\nhermes_cli/auth.py\ncli-config.yaml.example]
user --> cli
user --> gateway
user --> acp
user --> cron
user --> batch
cli --> agent
gateway --> agent
acp --> agent
cron --> agent
batch --> agent
agent --> model_tools
agent --> prompt
model_tools --> tools
agent --> state
cli --> config
gateway --> state
cron --> state
```
## Entry Points and Data Flow
### Primary entry points
- `run_agent.py`
- home of `AIAgent`
- contains the core conversation loop that alternates between model calls and tool execution
- `cli.py`
- terminal-first TUI wrapper around the agent loop
- wires prompt_toolkit input, Rich rendering, slash commands, and session management
- `hermes_cli/main.py`
- shared entry point for `hermes` subcommands
- operational front door for install, setup, gateway, tools, skills, config, auth, model switching, and doctor flows
- `gateway/run.py`
- runtime for Telegram, Discord, Slack, WhatsApp, Signal, email, and other platform adapters
- responsible for platform command dispatch and session continuity outside the CLI
- `acp_adapter/server.py`
- ACP server for IDE/editor integrations such as VS Code / Zed / JetBrains style transports
- `cron/scheduler.py`
- scheduler runtime for natural-language cron tasks and autonomous delivery
- `batch_runner.py`
- large batch-processing surface for parallelized, research-scale or evaluation-scale workloads
### Entry-point data flow
1. A surface entry point (`cli.py`, `gateway/run.py`, `acp_adapter/server.py`, `cron/scheduler.py`, or `batch_runner.py`) receives a task, chat message, or scheduled prompt.
2. That surface initializes `AIAgent` from `run_agent.py` with the current config, enabled toolsets, provider/model settings, callbacks, and session state.
3. `AIAgent.run_conversation()` builds a message list and enters a synchronous loop:
- ask the active model for a response
- if the model emits tool calls, dispatch through `model_tools.py`
- append tool results as tool messages
- continue until a final assistant message is produced or iteration limits are reached
4. Prompt shaping and memory support pass through the agent layer, especially:
- `agent/prompt_builder.py`
- `agent/context_compressor.py`
- `agent/prompt_caching.py`
- model metadata / auxiliary model helpers under `agent/`
5. Tool execution resolves through `tools/registry.py`, which discovers, registers, and dispatches tool handlers from the `tools/` package.
6. Session persistence and transcript search are handled by `hermes_state.py`, which uses SQLite + FTS5 and WAL mode.
7. Results are returned back to the entry surface, which formats and delivers them to the terminal, platform, or API caller.
### Practical runtime truth
- CLI, gateway, ACP, cron, and batch are not independent products. They are multiple shells around the same agent loop.
- The tool system is the actual center of gravity. Once initialized, most interesting behavior routes through `model_tools.py` + `tools/registry.py` + concrete tool modules.
- The persistence layer is shared infrastructure. `hermes_state.py` is not optional plumbing — it is the backbone for session continuity, search, and multi-surface coordination.
## Key Abstractions
### `AIAgent` in `run_agent.py`
This is the core abstraction of the repository.
Key responsibilities:
- hold runtime provider/model configuration
- manage per-run conversation state
- invoke the selected model backend
- process tool calls in a bounded loop
- return normalized final responses and message traces
The whole repo composes around this abstraction.
### `model_tools.py`
This is the tool/model orchestration seam.
It bridges:
- tool schema discovery
- tool-call handling
- enabled/disabled toolsets
- model-specific response normalization
The repo's “agentic” behavior is inseparable from this file.
### `tools/registry.py`
This is the central tool registry.
The design pattern is:
- one implementation file per tool or tool family under `tools/`
- each module registers schemas and handlers at import time
- the registry becomes the canonical dispatch table used everywhere else
This is one of the repo's strongest architectural abstractions because it lets every runtime surface share the same tool inventory.
### `toolsets.py`
The toolset layer groups concrete tools into named enable/disable bundles. It is the policy seam between raw tool capability and runtime policy.
### `hermes_state.py`
This file is both a session database and a concurrency design document.
Notable grounded traits:
- SQLite session store
- FTS5 search
- WAL mode
- explicit commentary about write-lock contention and checkpoint behavior
This file is security-, reliability-, and UX-critical.
### `hermes_cli/`
This subtree is the second major abstraction boundary. It contains the user-facing shell product:
- config loading and migration
- auth/provider resolution
- setup wizard
- slash command registry
- skins/themes
- model switching
- tools/skills configuration
The CLI is not a thin wrapper. It is a real product layer.
### `gateway/`
The gateway is another strong subsystem, not a helper. It handles platform adapters, authorization, routing, session persistence, and response delivery. This is how Hermes escapes the terminal and becomes a persistent messaging agent.
### `acp_adapter/`
This subtree exposes Hermes as an ACP-compatible service for IDE/editor tooling. It is a separate integration contract with its own tests and import requirements.
### `cron/`
This is the automation abstraction. Cron jobs are self-contained scheduled sessions that use the same agent stack but with fresh runtime context and delivery semantics.
## API Surface
### Human-facing command surface
From the README and AGENTS-guided structure, major entry commands include:
- `hermes`
- `hermes model`
- `hermes tools`
- `hermes config set`
- `hermes gateway`
- `hermes setup`
- `hermes claw migrate`
- `hermes update`
- `hermes doctor`
### Core Python surfaces to name explicitly
These are the most important code-bearing surfaces and should stay explicitly named in the genome:
- `run_agent.py`
- `model_tools.py`
- `tools/registry.py`
- `toolsets.py`
- `cli.py`
- `hermes_cli/main.py`
- `hermes_state.py`
- `gateway/run.py`
- `acp_adapter/server.py`
- `cron/scheduler.py`
- `batch_runner.py`
### Tool API surface
Notable tool modules called out by architecture and tests:
- `approval.py`
- `file_tools.py`
- `mcp_tool.py`
- terminal/process tools
- delegation / browser / web / code execution tools
These are not add-ons. They are part of the public behavioral contract of the agent.
### Platform surface
`gateway/platforms/` exposes platform-specific adapters. This is the repo's cross-channel delivery API boundary.
### Config surface
Important config consumers and sources:
- `hermes_cli/config.py`
- `cli-config.yaml.example`
- provider credential resolution under `hermes_cli/auth.py`
- runtime provider selection used by CLI and gateway layers
## Test Coverage Gaps
### Current test-scale truth
Grounded evidence from the analyzed checkout:
- collect-only command run: `python3 -m pytest tests --collect-only -q`
- result: `11850/11900 tests collected (50 deselected), 7 errors in 18.84s`
This means the repo has an enormous intended test surface, but clean collection on `main` is currently broken.
### Current collection failures
Observed collect-time failures:
- six ACP-related collection errors caused by `ModuleNotFoundError: No module named `acp``
- `tests/acp/test_entry.py`
- `tests/acp/test_events.py`
- `tests/acp/test_mcp_e2e.py`
- `tests/acp/test_permissions.py`
- `tests/acp/test_server.py`
- `tests/acp/test_tools.py`
- one separate collection failure:
- `tests/test_skill_manager_error_context.py` importing missing private helpers from `tools.skill_manager_tool`
Tracked findings:
- `hermes-agent#779` — ACP test collection fails without the `acp` extra; also mentions the unregistered `ssh` mark warning
- `hermes-agent#916` — skill manager error-context test imports missing private helpers
### Important test-gap tokens to preserve
These terms should remain in the artifact because they capture the current health signal:
- `11850/11900 tests collected`
- `7 errors`
- `ModuleNotFoundError: No module named acp`
- `trajectory_compressor.py`
- `batch_runner.py`
Why the last two matter:
- `trajectory_compressor.py` is a high-leverage training / research path that deserves explicit genome mention even when not the current collection blocker
- `batch_runner.py` is one of the repo's largest execution surfaces and should not disappear from architecture coverage
### High-risk untestable seams
- ACP/IDE integration currently requires optional extras to collect cleanly
- skill manager internal API drift broke at least one direct test contract
- platform adapters and transport-specific integrations are broad enough that test presence alone does not guarantee healthy runtime integration
- huge central modules increase the chance that tests exist but are incomplete relative to real complexity
## Security Considerations
### Command and approval boundary
`approval.py` is a first-order security surface. The repo is explicitly designed to mediate dangerous command execution, not just run commands blindly.
### Tool power surface
`file_tools.py`, `mcp_tool.py`, terminal/process orchestration, and delegation surfaces together create a very high-privilege agent runtime. Misconfiguration or prompt-shaping bugs here translate directly into real-world side effects.
### Prompt construction and model steering
`agent/prompt_builder.py` and related agent-layer modules are security-critical because they define what the model actually sees and how system, user, memory, skill, and tool context are assembled.
### Session persistence
`hermes_state.py` is security-relevant beyond reliability. The repo stores conversation history in SQLite + FTS5 and uses WAL mode. That improves concurrency but also makes session storage a long-lived trust surface.
### Optional integration extras
The ACP failures show a security/packaging truth: certain privileged integrations are optional and can break test collection when their dependencies are absent. That is both a packaging issue and a trust-boundary issue.
### Prompt caching and context mutation
Prompt caching and context compression are not mere performance tweaks. They are integrity-sensitive transforms on the conversation context. If they are wrong, the agent can drift semantically while appearing to work.
## Performance Characteristics
### Scale
- `3207` text files in the analyzed checkout
- `409,718` Python LOC from raw file counting
- `11850/11900` tests collected before collection errors interrupted the run
This is a very large Python codebase, not a boutique single-agent script.
### Performance-relevant architectural tokens to preserve
These are central to understanding repo performance behavior:
- `WAL mode`
- `prompt caching`
- `context compression`
- `parallel tool execution`
Grounded examples:
- `hermes_state.py` explicitly documents WAL mode, checkpoints, and write-lock contention
- AGENTS.md names `agent/prompt_caching.py` and `agent/context_compressor.py` as core internals
- the tool/delegation/batch surfaces make parallel tool execution a real architectural concern, not just a future enhancement
### Human and maintenance performance bottlenecks
The core bottleneck is centralization of complexity into giant multi-responsibility files:
- `run_agent.py`
- `model_tools.py`
- `cli.py`
- `hermes_state.py`
- `batch_runner.py`
- `mcp_tool.py`
- `cron/scheduler.py`
The repo is powerful because it centralizes these concerns, but it is also fragile for the same reason.
## Critical Modules to Name Explicitly
These modules should remain explicit in any future genome refresh because they are structural, not incidental:
- `run_agent.py`
- `model_tools.py`
- `tools/registry.py`
- `toolsets.py`
- `cli.py`
- `hermes_cli/main.py`
- `hermes_state.py`
- `gateway/run.py`
- `acp_adapter/server.py`
- `cron/scheduler.py`
- `agent/prompt_builder.py`
- `approval.py`
- `file_tools.py`
- `mcp_tool.py`
- `batch_runner.py`
- `trajectory_compressor.py`
## Key Findings to Preserve
- `hermes-agent` is the actual core framework under Timmy and other hosted agents
- the repo does **not** currently ship a `GENOME.md` on target `main`
- AGENTS.md is unusually important here because it documents the real architecture, file dependency chain, and entry points in a way the README does not
- the repo's shared persistence backbone is `hermes_state.py` with SQLite + FTS5 + WAL mode
- clean test collection is currently broken by optional ACP dependency gaps already tracked in `#779`
- an additional non-ACP test collection failure is now tracked in `#916`
- `run_agent.py`, `model_tools.py`, and `tools/registry.py` form the heart of the actual agent loop
- the repo's scale (`3207` text files, `409,718` Python LOC, `11850/11900` collected tests before failure) makes it a platform codebase, not a single-feature app

View File

@@ -28,11 +28,6 @@ def test_the_door_genome_has_required_sections() -> None:
def test_the_door_genome_captures_repo_specific_findings() -> None:
content = _content()
assert "19 Python files" in content
assert "146 passed, 3 subtests passed" in content
assert "crisis/session_tracker.py" in content
assert "tests/test_session_tracker.py" in content
assert "tests/test_false_positive_fixes.py" in content
assert "lastUserMessage" in content
assert "localStorage" in content
assert "crisis-offline.html" in content

View File

@@ -1,15 +1,15 @@
from pathlib import Path
GENOME = Path('GENOME.md')
GENOME = Path('hermes-agent-GENOME.md')
def read_genome() -> str:
assert GENOME.exists(), 'GENOME.md must exist at repo root'
assert GENOME.exists(), 'hermes-agent-GENOME.md must exist at repo root'
return GENOME.read_text(encoding='utf-8')
def test_genome_exists():
assert GENOME.exists(), 'GENOME.md must exist at repo root'
assert GENOME.exists(), 'hermes-agent-GENOME.md must exist at repo root'
def test_genome_has_required_sections():
@@ -55,9 +55,9 @@ def test_genome_mentions_control_plane_modules():
def test_genome_mentions_test_gap_and_collection_findings():
text = read_genome()
for token in [
'11,470 tests collected',
'6 collection errors',
'ModuleNotFoundError: No module named `acp`',
'11850/11900 tests collected',
'7 errors',
'ModuleNotFoundError: No module named',
'trajectory_compressor.py',
'batch_runner.py',
]:

View File

@@ -11,11 +11,10 @@ The Door is a crisis-first front door to Timmy: one URL, no account wall, no app
What the codebase actually contains today:
- 1 primary browser app: `index.html`
- 4 companion browser assets/pages: `about.html`, `testimony.html`, `crisis-offline.html`, `sw.js`
- 19 Python files across canonical crisis logic, session tracking, legacy shims, wrappers, and tests
- 5 tracked pytest files under `tests/`
- 17 Python files across canonical crisis logic, legacy shims, wrappers, and tests
- 2 Gitea workflows: `smoke.yml`, `sanity.yml`
- 1 systemd unit: `deploy/hermes-gateway.service`
- full test suite currently passing: `146 passed, 3 subtests passed`
- full test suite currently passing: `115 passed, 3 subtests passed`
The repo is small, but it is not simple. The true architecture is a layered safety system:
1. immediate browser-side crisis escalation
@@ -45,10 +44,8 @@ graph TD
H --> G[crisis/gateway.py]
G --> D[crisis/detect.py]
G --> S[crisis/session_tracker.py]
G --> R[crisis/response.py]
D --> CR[CrisisDetectionResult]
S --> SS[SessionState / CrisisSessionTracker]
R --> RESP[CrisisResponse]
D --> LEG[Legacy shims\ncrisis_detector.py\ncrisis_responder.py\ndying_detection]
@@ -81,10 +78,8 @@ graph TD
- canonical detection engine and public detection API
- `crisis/response.py`
- canonical response generator, UI flags, prompt modifier, grounding helpers
- `crisis/session_tracker.py`
- in-memory session escalation/de-escalation tracking and session-aware prompt modifiers
- `crisis/gateway.py`
- integration layer for `check_crisis()`, `check_crisis_with_session()`, and `get_system_prompt()`
- integration layer for `check_crisis()` and `get_system_prompt()`
- `crisis/compassion_router.py`
- profile-based prompt routing abstraction parallel to `response.py`
- `crisis_detector.py`
@@ -171,25 +166,7 @@ In `crisis/response.py`, the canonical response dataclass ties backend detection
- `provide_988`
- `escalate`
### 6. `CrisisSessionTracker` and `SessionState`
`crisis/session_tracker.py` adds a privacy-first in-memory session layer on top of per-message detection:
- `SessionState`
- `current_level`
- `peak_level`
- `message_count`
- `level_history`
- `is_escalating`
- `is_deescalating`
- `escalation_rate`
- `consecutive_low_messages`
- `CrisisSessionTracker`
- `record()` for per-message updates
- `get_session_modifier()` for prompt augmentation
- `get_ui_hints()` for frontend-facing advisory state
This is the clearest new architecture addition since the earlier genome pass: The Door now reasons about trajectory within a conversation, not just isolated message severity.
### 7. Legacy compatibility layer
### 6. Legacy compatibility layer
The repo still carries older interfaces:
- `crisis_detector.py`
- `crisis_responder.py`
@@ -200,7 +177,7 @@ These preserve compatibility, but they also create drift risk:
- two different `CrisisResponse` contracts
- two prompt-routing paths (`response.py` vs `compassion_router.py`)
### 8. Browser persistence contract
### 7. Browser persistence contract
`localStorage` is a real part of runtime state despite some docs claiming otherwise.
Keys:
- `timmy_chat_history`
@@ -238,11 +215,7 @@ Expected response shape:
- `crisis.response.generate_response(detection)`
- `crisis.response.process_message(text)`
- `crisis.response.get_system_prompt_modifier(detection)`
- `crisis.session_tracker.CrisisSessionTracker.record(detection)`
- `crisis.session_tracker.CrisisSessionTracker.get_session_modifier()`
- `crisis.session_tracker.check_crisis_with_session(text, tracker=None)`
- `crisis.gateway.check_crisis(text)`
- `crisis.gateway.check_crisis_with_session(text, tracker=None)`
- `crisis.gateway.get_system_prompt(base_prompt, text="")`
- `crisis.gateway.format_gateway_response(text, pretty=True)`
@@ -256,13 +229,12 @@ Expected response shape:
### Current state
Verified on fresh `main` clone of `the-door`:
- `python3 -m pytest -q` -> `146 passed, 3 subtests passed`
- `python3 -m pytest -q` -> `115 passed, 3 subtests passed`
What is already covered well:
- canonical crisis detection tiers
- response flags and gateway structure
- many false-positive regressions (`tests/test_false_positive_fixes.py`)
- session escalation/de-escalation tracking (`tests/test_session_tracker.py`)
- many false-positive regressions
- service-worker offline crisis fallback
- crisis overlay focus trap string-level assertions
- deprecated wrapper behavior
@@ -427,7 +399,7 @@ The repo's deploy surface is not fully coherent:
7. Align or remove resilience scripts targeting the wrong port/service.
8. Resolve doc drift:
- ARCHITECTURE says “close tab = gone,” but implementation uses `localStorage`
- BACKEND_SETUP still says 49 tests, while current verified suite is 146 + 3 subtests
- BACKEND_SETUP still says 49 tests, while current verified suite is 115 + 3 subtests
- audit docs understate current automation coverage
### Strategic debt