Every chat session now runs through the Hermes bridge, so the three chat chains become feature-equivalent instead of three partially-maintained dispatch paths. Keeping the coding-agent and in-house agent runtimes alive meant every bridge change had to be mirrored into two more runtimes that had already drifted. Historical rows are read, not migrated: sessions stored with the retired source normalize to cli on load, workflow graphs naming the retired agents keep loading and run on the bridge, and the visible-source check plus SOURCE_LABELS still recognize the old value so those sessions stay listed. The public surface follows the code: the OpenAPI generator and the MCP chat-run tool schema drop the six retired entry fields so external callers are not told about runtimes that no longer exist. harness-check gains a RETIRED_RUNTIME scan so the residual check is a mechanical gate instead of a manual grep, with the two surviving read-side compat sites recorded with reasons. Refs: docs/phases/phase_003.md
5.4 KiB
5.4 KiB
Architecture
hermes-ui is a TypeScript project that ships a browser dashboard and a Koa backend around Hermes Agent. It runs on Node.js and talks to Hermes over an Agent Bridge (see "Hermes Integration" below).
Package Boundaries
| Area | Path | Responsibility |
|---|---|---|
| Client | packages/client/src |
Vue UI, routing, Pinia stores, API wrappers, i18n, browser-visible state. |
| Server | packages/server/src |
HTTP API, auth, Socket.IO, SQLite stores, file access, Hermes runtime integration. |
| Skills | packages/skills |
Built-in skills injected into the Hermes skills directory. |
| Tests | tests |
Vitest unit/integration tests and Playwright browser tests. |
| CI | .github/workflows |
Build, e2e, lockfile, Docker, and release automation. |
This is not an npm workspaces monorepo. packages/client and
packages/server have no package.json; their dependencies are declared in the
root package.json.
Request Flow
- The browser loads the Vite-built client from the Koa server.
- Client modules call API helpers from
packages/client/src/api. - Server routes in
packages/server/src/routeswire HTTP paths to controllers. - Controllers validate request concerns and delegate reusable behavior to services.
- Services own side effects: files, SQLite, Hermes profiles, subprocesses, bridges, and credentials.
- Long-running chat and group-chat flows use Socket.IO namespaces managed by server services.
Keep each layer narrow. Routes should not grow business logic, and client code should not duplicate server persistence rules.
State And Data Ownership
- Web UI state defaults to
~/.hermes-web-uithroughconfig.appHome. HERMES_WEB_UI_HOMEandHERMES_WEBUI_STATE_DIRoverride Web UI state location. These identifiers are intentionally kept from the 0.6.x line: renaming them would break existing deployment scripts and orphan existing data directories.- Hermes Agent state lives under Hermes profile directories and must stay distinct
from Web UI state. Profile isolation is done by injecting
HERMES_HOMEinto child processes, never by copying directories. - Hermes
state.dbis read-only and owned by the Hermes Agent. Node-side code must never open it; read session history through the bridge'sgetHistory/getOutput. - The project's own SQLite store under
packages/server/src/dbis the only writable store for sessions, messages, and usage. There is no startup import sync from Hermes state. - Uploads default to
config.uploadDir, which is derived from the Web UI home unlessUPLOAD_DIRis set. - Runtime data directories must also live under the Web UI home, not beside built
distassets. - Profile-scoped Hermes data should use existing profile helpers instead of manually joining paths.
Server Structure
routes/registers HTTP and WebSocket entry points.controllers/handles request-level behavior.services/owns reusable IO, domain behavior, external process calls, and integration logic.db/owns SQLite schemas and stores.middleware/owns request middleware such as user auth.shared/contains cross-server constants and helpers.
Architecture rules:
- Register local API routes before proxy catch-all routes.
- Keep auth behavior centralized in
packages/server/src/services/auth.ts. - Prefer
execFileorspawnwith argument arrays over shell command strings. - All Hermes CLI calls go through
execHermesWithBin/spawnHermesWithBininservices/hermes/hermes-process.ts. - Use structured file and YAML/JSON parsers when editing structured data.
Hermes Integration
Chat runs through the Agent Bridge, which is the only supported path:
browser --Socket.IO--> Koa/Node server --JSON over Unix socket--> Python child
└- hermes-agent run_agent.AIAgent
services/hermes/agent-bridge/python/is vendored, read-only code. The only exception is theHERMES_DESKTOPchecks inbridge_runtime.py, which hide the Windows console window for git subprocesses and must stay.services/hermes/group-chat/,lib/context-compressor/, andservices/hermes/context-engine/form the chat chain. Changes there require adocs/chat-chain-changes/<date>-<slug>.mdfragment.- The Hermes CLI is user-installed. Point
HERMES_BINat the executable, or let the server resolvehermesfromPATH.
Client Structure
views/contains route-level screens.components/contains reusable UI.stores/contains Pinia state.api/contains HTTP clients and should usepackages/client/src/api/client.ts.i18n/contains locale messages for user-facing strings. Onlyzhandenare supported.styles/contains global styling and theme primitives.
Frontend rules:
- Use Vue 3 Composition API with
<script setup lang="ts">. - Use Arco Design Vue components. Naive UI is being removed; do not add new
naive-uiimports. - Add visible text to every locale file.
- Keep component styles scoped unless the style is intentionally global.
Validation Surface
The minimum mechanical harness is:
npm run harness:checkfor repository docs, workflow, and package-script invariants.npm run testor focused Vitest tests for local logic.npm run test:e2efor browser-visible routing/auth/chat regressions.npm run buildfor type checking and production bundles.
See docs/harness/validation.md for change-specific commands.