From 31fcdfa39a2254aa791be28309d525dc6e4e6adc Mon Sep 17 00:00:00 2001 From: nesquena-hermes <[email protected]> Date: Mon, 1 Jun 2026 01:42:20 +0000 Subject: [PATCH 1/5] docs: refresh + reorganize README and key markdown files README: - Add a Contents table of contents for navigability (800-line doc). - Freshen stale snapshots: test count 5303/488 files -> ~7,150/~700 files; reframe contributor counts to point at CONTRIBUTORS.md as the live source. - Rebuild the Architecture section: drop per-file exact LOC (drifts every release -> chronic staleness) in favor of a stable backend/frontend role map; add pyproject.toml + the ruff/browser/docker CI gates. - Reorganize the Docs index by purpose (Start here / Using / Deploying / Contributing & design / Release history); add missing CONTRIBUTING.md, DESIGN.md, docs/workspace-git.md; convert bare paths to working links. ARCHITECTURE.md: header v0.51.54/5303 -> v0.51.192/~7,150 + note that the numbers are a periodic snapshot (authoritative source = git tag + collect-only). TESTING.md: header + footer test counts refreshed; drop the stale 'through v0.50.21' framing; note the ruff/browser/docker gates. ROADMAP.md: refresh the 'Last updated' stamp (v0.51.31/5028 -> v0.51.192/~7,150) with recent themes. Markdown-only; all internal links verified to resolve. --- ARCHITECTURE.md | 8 ++- README.md | 142 ++++++++++++++++++++++++++++++++---------------- ROADMAP.md | 2 +- TESTING.md | 8 +-- 4 files changed, 104 insertions(+), 56 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 98946a4e9..8f6e5abbb 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -7,10 +7,12 @@ > > Keep this document updated as architecture changes are made. -> Current shipped build: `v0.51.54` (May 13, 2026). -> Automated coverage: 5303 tests via `pytest tests/ --collect-only -q`. CI runs on Python 3.11, 3.12, and 3.13 against every PR. +> Current shipped build: `v0.51.192` (May 31, 2026). +> Automated coverage: ~7,150 tests via `pytest tests/ --collect-only -q`. CI runs on +> Python 3.11, 3.12, and 3.13 (3 parallel shards each) against every PR, plus a ruff +> lint gate, a headless browser smoke test, and a Docker smoke test. > -> Notable architecture state as of v0.51.54: the bootstrap and first-run onboarding flow own setup discovery; the default WebUI state directory is `~/.hermes/webui`; `ctl.sh` provides a daemon wrapper for homelab installs; chat streaming is still WebUI-owned SSE with stream-ownership guards, cancellation, async manual compression, and turn-journal audit plumbing; provider/model discovery is profile-aware with live-model cache invalidation and custom-provider scoping. +> Notable architecture state: the bootstrap and first-run onboarding flow own setup discovery; the default WebUI state directory is `~/.hermes/webui`; `ctl.sh` provides a daemon wrapper for homelab installs; chat streaming is still WebUI-owned SSE with stream-ownership guards, cancellation, async manual compression, and turn-journal audit plumbing; provider/model discovery is profile-aware with live-model cache invalidation and custom-provider scoping. (Version/test-count numbers above are a periodic snapshot — the authoritative source is the latest git tag and `pytest --collect-only`.) --- diff --git a/README.md b/README.md index 567339cbe..447709a3f 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,20 @@ This gives you nearly **1:1 parity with Hermes CLI from a convenient web UI** wh --- +## Contents + +- [Why Hermes](#why-hermes) — what it is and how it compares +- [Quick start](#quick-start) — clone + `bootstrap.py` / `start.sh` / `ctl.sh` +- [Docker](#docker) — single- and multi-container deploys +- [Configuration & access](#what-startsh-discovers-automatically) — auto-discovery, overrides, remote/Tailscale/phone, manual launch +- [Running tests](#running-tests) +- [Features](#features) — chat, sessions, workspace, voice, profiles, security, themes, panels, mobile +- [Architecture](#architecture) — backend/frontend layout, state dir +- [Docs](#docs) — the full documentation index +- [Contributors](#contributors) + +--- + ## Why Hermes Most AI tools reset every session. They don't know who you are, what you worked on, or what @@ -480,7 +494,8 @@ Or using the agent venv explicitly: Tests run against an isolated server with a separate state directory. Production data and real cron jobs are never touched. Current snapshot: -**5303 tests collected** across **488 test files**. +**~7,150 tests collected** across **~700 test files**, run in CI on Python 3.11, +3.12, and 3.13 (3 parallel shards each). --- @@ -607,69 +622,100 @@ Production data and real cron jobs are never touched. Current snapshot: ## Architecture +No build step, no framework, no bundler — a Python standard-library HTTP server +and vanilla JS. The backend lives in `api/`, the frontend in `static/`. + +**Backend (`api/`)** + ``` -server.py HTTP routing shell + auth middleware (~446 lines) +server.py HTTP routing shell + auth middleware api/ - auth.py Optional password authentication, signed cookies (~366 lines) - config.py Discovery, globals, model detection, reloadable config (~4139 lines) - helpers.py HTTP helpers, security headers (~302 lines) - models.py Session model + CRUD + CLI bridge (~1927 lines) - onboarding.py First-run onboarding wizard, OAuth provider support (~1002 lines) - profiles.py Profile state management, hermes_cli wrapper (~1056 lines) - routes.py All GET + POST route handlers (~9772 lines) - state_sync.py /insights sync — message_count to state.db (~118 lines) - streaming.py SSE engine, run_agent, cancel support (~4420 lines) - updates.py Self-update check and release notes (~545 lines) - upload.py Multipart parser, file upload handler (~284 lines) - workspace.py File ops, workspace helpers, git detection (~810 lines) -static/ - index.html HTML template (~1323 lines) - style.css All CSS incl. mobile responsive, themes (~3767 lines) - ui.js DOM helpers, renderMd, tool cards, context indicator (~7216 lines) - workspace.js File preview, file ops, git badge (~369 lines) - sessions.js Session CRUD, collapsible groups, search, reload recovery (~3517 lines) - messages.js send(), SSE handlers, live streaming, session recovery (~2301 lines) - panels.js Cron, skills, memory, profiles, settings (~6480 lines) - commands.js Slash command autocomplete (~1302 lines) - boot.js Mobile nav, voice input, boot IIFE (~1607 lines) -tests/ - conftest.py Isolated test server/state fixtures - 488 test files 5303 tests collected -Dockerfile python:3.12-slim container image -docker-compose.yml Compose with named volume and optional auth -.github/workflows/ CI: multi-arch Docker build + GitHub Release on tag + auth.py Optional password authentication, signed cookies, passkeys + config.py Discovery, globals, model detection, reloadable config + helpers.py HTTP helpers, security headers + models.py Session model + CRUD + CLI/state.db bridge + onboarding.py First-run onboarding wizard, OAuth provider support + profiles.py Profile state management, hermes_cli wrapper + routes.py All GET + POST route handlers (if/elif dispatch, no decorators) + state_sync.py /insights sync — message_count to state.db + streaming.py SSE engine, run_agent, cancellation, compression + updates.py Self-update check and release notes + upload.py Multipart parser, file upload handler + workspace.py File ops, workspace helpers, git detection +``` + +**Frontend (`static/`)** + +``` +index.html HTML template +style.css All CSS incl. mobile responsive, themes + skins +ui.js DOM helpers, renderMd, tool cards, context indicator +workspace.js File preview, file ops, git badge, central api() fetch wrapper +sessions.js Session CRUD, collapsible groups, search, reload recovery +messages.js send(), SSE handlers, live streaming, session recovery +panels.js Cron, skills, memory, profiles, settings (Control Center) +commands.js Slash command autocomplete +boot.js Mobile nav, voice input, theme/skin boot, bfcache handler +``` + +**Tests + packaging** + +``` +tests/ Pytest suite (~7,150 tests; isolated server/state fixtures) +pyproject.toml Tooling config (ruff lint gate) — not a packaged distribution +Dockerfile python:3.12-slim container image +docker-compose.yml Compose with named volume and optional auth +.github/workflows/ CI: ruff + sharded pytest, browser smoke, Docker smoke, + multi-arch Docker build + GitHub Release on tag ``` State lives outside the repo at `~/.hermes/webui/` by default (sessions, workspaces, settings, projects, last_workspace). Override with `HERMES_WEBUI_STATE_DIR`. +Full design notes and the endpoint catalog are in [`ARCHITECTURE.md`](ARCHITECTURE.md). --- ## Docs -- `docs/why-hermes.md` -- why Hermes, mental model, and detailed comparison to Claude Code / Codex / OpenCode / Cursor -- `ROADMAP.md` -- feature roadmap and sprint history -- `ARCHITECTURE.md` -- system design, all API endpoints, implementation notes -- `TESTING.md` -- manual browser test plan and automated coverage reference -- `CHANGELOG.md` -- release notes per sprint -- `SPRINTS.md` -- forward sprint plan with CLI + Claude parity targets -- `THEMES.md` -- theme system documentation, custom theme guide -- `docs/CONTRACTS.md` -- project contract/RFC/design index for contributors and agents -- `docs/UIUX-GUIDE.md` -- UI/UX principles sourced from existing design docs and visual inventories -- `docs/docker.md` -- Docker compose setup, common failures, and bind-mount migration -- `docs/supervisor.md` -- launchd, systemd, supervisord, runit, and s6 process-supervisor setup -- `docs/onboarding.md` -- first-run wizard, provider setup, local model server Base URLs, and safe re-runs -- `docs/onboarding-agent-checklist.md` -- safety rules, evidence commands, and pass/fail checks for assistant-led install or reinstall support -- `docs/troubleshooting.md` -- diagnostic flows for common failures (e.g. "AIAgent not available") -- `docs/wsl-autostart.md` -- WSL2 auto-start at Windows login -- `docs/EXTENSIONS.md` -- administrator-controlled WebUI extension injection -- `docs/rfcs/README.md` -- RFC index for larger architecture and durability proposals +**Start here** +- [`docs/why-hermes.md`](docs/why-hermes.md) — why Hermes, the mental model, and a detailed comparison to Claude Code / Codex / OpenCode / Cursor +- [`docs/onboarding.md`](docs/onboarding.md) — first-run wizard, provider setup, local model server Base URLs, and safe re-runs +- [`docs/troubleshooting.md`](docs/troubleshooting.md) — diagnostic flows for common failures (e.g. "AIAgent not available") + +**Using & customizing** +- [`THEMES.md`](THEMES.md) — theme + skin system, custom theme guide +- [`docs/workspace-git.md`](docs/workspace-git.md) — the workspace Git controls +- [`docs/EXTENSIONS.md`](docs/EXTENSIONS.md) — administrator-controlled WebUI extension injection + +**Deploying & operating** +- [`docs/docker.md`](docs/docker.md) — Docker compose setup, common failures, and bind-mount migration +- [`docs/supervisor.md`](docs/supervisor.md) — launchd, systemd, supervisord, runit, and s6 process-supervisor setup +- [`docs/wsl-autostart.md`](docs/wsl-autostart.md) — WSL2 auto-start at Windows login +- [`docs/onboarding-agent-checklist.md`](docs/onboarding-agent-checklist.md) — safety rules and pass/fail checks for assistant-led install/reinstall support + +**Contributing & design** +- [`CONTRIBUTING.md`](CONTRIBUTING.md) — contribution style, PR expectations, and local verification +- [`ARCHITECTURE.md`](ARCHITECTURE.md) — system design, all API endpoints, implementation notes +- [`TESTING.md`](TESTING.md) — manual browser test plan and automated coverage reference +- [`DESIGN.md`](DESIGN.md) — design tokens and the calm-console direction +- [`docs/UIUX-GUIDE.md`](docs/UIUX-GUIDE.md) — UI/UX principles sourced from the design docs and visual inventories +- [`docs/CONTRACTS.md`](docs/CONTRACTS.md) — project contract/RFC/design index for contributors and agents +- [`docs/rfcs/README.md`](docs/rfcs/README.md) — RFC index for larger architecture and durability proposals + +**Release history & plan** +- [`CHANGELOG.md`](CHANGELOG.md) — release notes per version +- [`ROADMAP.md`](ROADMAP.md) — feature roadmap and sprint history +- [`SPRINTS.md`](SPRINTS.md) — forward sprint plan with CLI + Claude parity targets +- [`CONTRIBUTORS.md`](CONTRIBUTORS.md) — the full community credit roll ## Contributors Hermes WebUI is built with help from the open-source community. Every PR — whether merged directly, absorbed into a batch release, or salvaged from a larger proposal — shapes the project, and we're grateful to everyone who has taken the time to contribute. -**137 contributors have shipped code that landed in a release tag** as of v0.51.58. The full credit roll lives in [`CONTRIBUTORS.md`](CONTRIBUTORS.md). The highlights: +Over **137 contributors** have shipped code that landed in a release tag. The full, +continuously-updated credit roll — including everyone with one or two PRs and the +special-thanks roll for design and architectural work — lives in +[`CONTRIBUTORS.md`](CONTRIBUTORS.md). A snapshot of the most prolific contributors: ### Top contributors (by PR count, including absorbed/batch-released work) diff --git a/ROADMAP.md b/ROADMAP.md index 2f07e7799..4aceb531a 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,7 +2,7 @@ > Web companion to the Hermes Agent CLI. Same workflows, browser-native. > -> Last updated: v0.51.31 (May 9, 2026) — 5028 tests collected — Release H 12-PR contributor batch (image-mode fix + race fixes + composer drafts + locale parity + custom-provider dedup + TTL config + heartbeat polish) +> Last updated: v0.51.192 (May 31, 2026) — ~7,150 tests collected. Recent themes: forward-looking ruff lint gate (#3273), Windows upgrade state-stranding hotfix (#2905), per-model context_length default-only guard (#3256), gateway-configured banner + tooltip i18n, and an ongoing concentric PR-triage cadence. > Test source: `pytest tests/ --collect-only -q` > Per-version detail: see [CHANGELOG.md](./CHANGELOG.md) diff --git a/TESTING.md b/TESTING.md index ead582689..6f5fb59b5 100644 --- a/TESTING.md +++ b/TESTING.md @@ -1,14 +1,14 @@ # Hermes Web UI: Browser Testing Plan > This document is for manual browser testing by you or by a Claude browser agent. -> It covers user-facing features of the UI through v0.50.21 and later releases. +> It covers user-facing features of the UI across current releases. > Each section is written as a step-by-step test procedure with expected outcomes. > A browser agent (e.g. Claude with Chrome access) can execute this plan directly. > > Prerequisites: SSH tunnel is active on port 8787. Open http://localhost:8787 in browser. > Server health check: curl http://127.0.0.1:8787/health should return {"status":"ok"}. > -> Automated coverage: 5303 tests collected via `pytest tests/ --collect-only -q`. Tests run on every PR via GitHub Actions on Python 3.11, 3.12, and 3.13. The suite covers the bootstrap/static wizard, real provider config persistence (`config.yaml` + `.env`), the `/api/onboarding/*` backend, the onboarding skip/existing-config guard, CSS regression coverage for thinking/tool card animation, streaming session persistence, mobile layout breakpoints, locale parity across 11 languages, and hundreds of issue/PR-pinned regression tests. +> Automated coverage: ~7,150 tests collected via `pytest tests/ --collect-only -q`. Tests run on every PR via GitHub Actions on Python 3.11, 3.12, and 3.13 (3 parallel shards each), alongside a ruff lint gate, a headless browser smoke test, and a Docker smoke test. The suite covers the bootstrap/static wizard, real provider config persistence (`config.yaml` + `.env`), the `/api/onboarding/*` backend, the onboarding skip/existing-config guard, CSS regression coverage for thinking/tool card animation, streaming session persistence, mobile layout breakpoints, locale parity across 11 languages, and hundreds of issue/PR-pinned regression tests. > Run: `pytest tests/ -v --timeout=60` > > Local regression focus: verify that a previously closed workspace panel stays visually closed from first paint through boot completion on desktop refresh; there should be no brief open-then-close flash. @@ -1925,8 +1925,8 @@ Bridged CLI sessions: --- -*Last updated: v0.51.54, May 13, 2026* -*Total automated tests collected: 5303* +*Last updated: v0.51.192, May 31, 2026* +*Total automated tests collected: ~7,150 (run `pytest tests/ --collect-only -q` for the exact current count)* *Regression gate: tests/test_regressions.py* *Run: pytest tests/ -v --timeout=60* *Source: /* From f14efc436896c0c43fedbac8de92ed99d01898d2 Mon Sep 17 00:00:00 2001 From: nesquena-hermes <[email protected]> Date: Mon, 1 Jun 2026 02:14:01 +0000 Subject: [PATCH 2/5] docs: regenerate contributor tally (137->194, union, nobody dropped) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regenerate CONTRIBUTORS.md + README contributors section from a verified 3-source union: GitHub merged-PR list, CHANGELOG.md attribution lines, and Co-authored-by trailers on master commits (the canonical signal for a CLOSED contributor PR whose commits were cherry-picked/absorbed and attributed). - New tally: 194 contributors / 843 PR credits (was a stale 137 / 646). - The increase: ~135 releases since the v0.51.58 pin PLUS newly-detected absorbed-CLOSED PRs the prior hand-count missed (e.g. franksong2702 148 = 129 merged + 19 cherry-picked-and-attributed). - UNION with the existing hand-curated file as a floor: 27 old contributors had no machine-readable signal (very old closed PRs) — preserved, ZERO dropped. - Refreshed special-thanks PR counts to match. - Generator committed to the maintainer workspace as scripts/regen_contributors.py (--merge-existing keeps it safe for all future refreshes). Verified: every one of the original 137 logins still present (+57 new). --- CONTRIBUTORS.md | 89 ++++++++++++++++++++++++++----------------------- README.md | 24 ++++++------- 2 files changed, 60 insertions(+), 53 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 4019a9c2a..8f81709e8 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,14 +1,14 @@ # Contributors -Hermes WebUI is a community project. **137 people** have shipped code that landed in a release tag — including the long tail of folks whose work was salvaged into batch releases or absorbed via Co-authored-by trailers. This file is the canonical credit roll. +Hermes WebUI is a community project. **194 people** have shipped code that landed in a release tag — including the long tail of folks whose work was salvaged into batch releases or absorbed via Co-authored-by trailers. This file is the canonical credit roll. -A contributor's PR count is the number of distinct PRs they get credit for: PRs they authored that merged directly, PRs they authored that were closed-but-absorbed into a release commit (batch merges, salvage rewrites), and PRs where they were explicitly attributed in `CHANGELOG.md`. All three count the same. +A contributor's PR count is the number of distinct PRs they get credit for: PRs they authored that merged directly, PRs they authored that were closed-but-absorbed into a release commit (batch merges, salvage rewrites, cherry-picked-and-attributed work), and PRs where they were explicitly attributed in `CHANGELOG.md`. All count the same. -**Total contributors tracked:** 137 -**Total PR credits:** 646 -**Last refreshed:** v0.51.58, 2026-05-13 +**Total contributors tracked:** 194 +**Total PR credits:** 843 +**Last refreshed:** v0.51.192, 2026-05-31 -Generated from `git log` + the GitHub PR list (merged and closed) + the `CHANGELOG.md` attribution lines (`PR #N by @user`, `(credit: @user)`, `@user — PR #N`). If your name is missing or wrong, open a PR against `CONTRIBUTORS.md` — we cross-check against the changelog on each release. +Generated by `scripts/regen_contributors.py` in the maintainer workspace, which unions three sources: the GitHub merged-PR list, `CHANGELOG.md` attribution lines, and `Co-authored-by:` trailers on commits that landed on master (the canonical signal for a CLOSED PR whose commits were cherry-picked in and attributed). If your name is missing or wrong, open a PR against `CONTRIBUTORS.md` — we cross-check against the changelog on each release. --- @@ -16,55 +16,62 @@ Generated from `git log` + the GitHub PR list (merged and closed) + the `CHANGEL | # | Contributor | PRs | First release | Latest release | |---|---|---:|---|---| -| 1 | [@franksong2702](https://github.com/franksong2702) | 117 | `v0.49.3` | `v0.51.58` | -| 2 | [@Michaelyklam](https://github.com/Michaelyklam) | 92 | `v0.50.240` | `v0.51.57` | -| 3 | [@bergeouss](https://github.com/bergeouss) | 62 | `v0.48.0` | `v0.51.46` | -| 4 | [@ai-ag2026](https://github.com/ai-ag2026) | 55 | `v0.50.279` | `v0.51.47` | -| 5 | [@dso2ng](https://github.com/dso2ng) | 23 | `v0.50.227` | `v0.51.51` | -| 6 | [@jasonjcwu](https://github.com/jasonjcwu) | 16 | `v0.50.227` | `v0.51.55` | -| 7 | [@Jordan-SkyLF](https://github.com/Jordan-SkyLF) | 12 | `v0.50.18` | `v0.51.58` | -| 8 | [@aronprins](https://github.com/aronprins) | 10 | `v0.44.0` | `v0.50.233` | -| 9 | [@JKJameson](https://github.com/JKJameson) | 10 | `v0.50.233` | `v0.51.31` | -| 10 | [@starship-s](https://github.com/starship-s) | 10 | `v0.50.128` | `v0.51.58` | -| 11 | [@ccqqlo](https://github.com/ccqqlo) | 9 | `v0.44.0` | `v0.50.270` | -| 12 | [@24601](https://github.com/24601) | 8 | `v0.50.233` | `v0.51.5` | -| 13 | [@armorbreak001](https://github.com/armorbreak001) | 7 | `v0.50.47` | `v0.50.50` | -| 14 | [@NocGeek](https://github.com/NocGeek) | 7 | `v0.50.251` | `v0.50.252` | -| 15 | [@dobby-d-elf](https://github.com/dobby-d-elf) | 6 | `v0.51.38` | `v0.51.58` | -| 16 | [@Hinotoi-agent](https://github.com/Hinotoi-agent) | 6 | `v0.50.12` | `v0.51.44` | -| 17 | [@iRonin](https://github.com/iRonin) | 6 | `v0.41.0` | `v0.41.0` | -| 18 | [@Sanjays2402](https://github.com/Sanjays2402) | 6 | `v0.50.292` | `v0.51.31` | -| 19 | [@cloudyun888](https://github.com/cloudyun888) | 5 | `v0.50.47` | `v0.50.140` | -| 20 | [@fxd-jason](https://github.com/fxd-jason) | 5 | `v0.50.245` | `v0.50.249` | -| 21 | [@happy5318](https://github.com/happy5318) | 5 | `v0.50.238` | `v0.51.31` | +| 1 | [@franksong2702](https://github.com/franksong2702) | 148 | `v0.49.3` | `v0.51.153` | +| 2 | [@Michaelyklam](https://github.com/Michaelyklam) | 117 | `v0.50.240` | `v0.51.139` | +| 3 | [@bergeouss](https://github.com/bergeouss) | 70 | `v0.48.0` | `v0.51.46` | +| 4 | [@ai-ag2026](https://github.com/ai-ag2026) | 67 | `v0.50.279` | `v0.51.190` | +| 5 | [@dso2ng](https://github.com/dso2ng) | 25 | `v0.50.227` | `v0.51.153` | +| 6 | [@AJV20](https://github.com/AJV20) | 24 | `v0.51.93` | `v0.51.188` | +| 7 | [@starship-s](https://github.com/starship-s) | 19 | `v0.50.123` | `v0.51.153` | +| 8 | [@jasonjcwu](https://github.com/jasonjcwu) | 16 | `v0.50.227` | `v0.51.132` | +| 9 | [@dobby-d-elf](https://github.com/dobby-d-elf) | 15 | `v0.51.38` | `v0.51.161` | +| 10 | [@Jordan-SkyLF](https://github.com/Jordan-SkyLF) | 12 | `v0.50.18` | `v0.51.66` | +| 11 | [@aronprins](https://github.com/aronprins) | 10 | `v0.44.0` | `v0.51.45` | +| 12 | [@JKJameson](https://github.com/JKJameson) | 10 | `v0.50.198` | `v0.51.31` | +| 13 | [@ccqqlo](https://github.com/ccqqlo) | 9 | `v0.44.0` | `v0.50.270` | +| 14 | [@24601](https://github.com/24601) | 8 | `v0.50.189` | `v0.51.5` | +| 15 | [@LumenYoung](https://github.com/LumenYoung) | 8 | `v0.51.47` | `v0.51.99` | +| 16 | [@Sanjays2402](https://github.com/Sanjays2402) | 8 | `v0.50.292` | `v0.51.158` | +| 17 | [@armorbreak001](https://github.com/armorbreak001) | 7 | `v0.50.47` | `v0.50.50` | +| 18 | [@NocGeek](https://github.com/NocGeek) | 7 | `v0.50.251` | `v0.50.252` | +| 19 | [@Hinotoi-agent](https://github.com/Hinotoi-agent) | 6 | `v0.50.10` | `v0.51.44` | +| 20 | [@iRonin](https://github.com/iRonin) | 6 | `v0.41.0` | `v0.41.0` | +| 21 | [@bsgdigital](https://github.com/bsgdigital) | 5 | `v0.50.159` | `v0.51.31` | +| 22 | [@cloudyun888](https://github.com/cloudyun888) | 5 | `v0.50.47` | `v0.50.140` | +| 23 | [@fxd-jason](https://github.com/fxd-jason) | 5 | `v0.50.245` | `v0.50.250` | +| 24 | [@george-andraws](https://github.com/george-andraws) | 5 | `v0.51.139` | `v0.51.153` | +| 25 | [@happy5318](https://github.com/happy5318) | 5 | `v0.50.238` | `v0.51.31` | ## Sustained contributors (3–4 PRs landed) | Contributor | PRs | First release | Latest release | |---|---:|---|---| -| [@bsgdigital](https://github.com/bsgdigital) | 4 | `v0.50.228` | `v0.50.258` | +| [@AlexeyDsov](https://github.com/AlexeyDsov) | 4 | `v0.50.267` | `v0.51.139` | | [@fecolinhares](https://github.com/fecolinhares) | 4 | `v0.50.238` | `v0.50.250` | | [@frap129](https://github.com/frap129) | 4 | `v0.50.140` | `v0.50.233` | +| [@Isla-Liu](https://github.com/Isla-Liu) | 4 | `v0.51.100` | `v0.51.142` | | [@KingBoyAndGirl](https://github.com/KingBoyAndGirl) | 4 | `v0.50.238` | `v0.50.240` | -| [@LumenYoung](https://github.com/LumenYoung) | 4 | `v0.51.47` | `v0.51.55` | -| [@qxxaa](https://github.com/qxxaa) | 4 | `v0.50.233` | `v0.51.37` | +| [@qxxaa](https://github.com/qxxaa) | 4 | `v0.50.210` | `v0.51.37` | | [@renheqiang](https://github.com/renheqiang) | 4 | `v0.50.61` | `v0.50.95` | | [@Thanatos-Z](https://github.com/Thanatos-Z) | 4 | `v0.50.257` | `v0.50.278` | -| [@AlexeyDsov](https://github.com/AlexeyDsov) | 3 | `v0.50.267` | `v0.50.278` | -| [@deboste](https://github.com/deboste) | 3 | `v0.50.269` | `v0.50.297` | +| [@deboste](https://github.com/deboste) | 3 | `v0.17.1` | `v0.50.297` | | [@dutchaiagency](https://github.com/dutchaiagency) | 3 | `v0.50.281` | `v0.50.286` | +| [@espokaos-ops](https://github.com/espokaos-ops) | 3 | `v0.51.92` | `v0.51.94` | | [@lucasrc](https://github.com/lucasrc) | 3 | `v0.51.57` | `v0.51.57` | +| [@mccxj](https://github.com/mccxj) | 3 | `v0.51.75` | `v0.51.148` | +| [@mysoul12138](https://github.com/mysoul12138) | 3 | `v0.51.161` | `v0.51.185` | +| [@pamnard](https://github.com/pamnard) | 3 | `v0.51.186` | `v0.51.191` | | [@pavolbiely](https://github.com/pavolbiely) | 3 | `v0.50.159` | `v0.50.233` | -## Two-PR contributors (19) +## Two-PR contributors (17) -[@ChaseFlorell](https://github.com/ChaseFlorell), [@gabogabucho](https://github.com/gabogabucho), [@hacker1e7](https://github.com/hacker1e7), [@hualong1009](https://github.com/hualong1009), [@lost9999](https://github.com/lost9999), [@michael-dg](https://github.com/michael-dg), [@mmartial](https://github.com/mmartial), [@MrFant](https://github.com/MrFant), [@nickgiulioni1](https://github.com/nickgiulioni1), [@renatomott](https://github.com/renatomott), [@ruxme](https://github.com/ruxme), [@Saik0s](https://github.com/Saik0s), [@samuelgudi](https://github.com/samuelgudi), [@shruggr](https://github.com/shruggr), [@TaraTheStar](https://github.com/TaraTheStar), [@vansour](https://github.com/vansour), [@vcavichini](https://github.com/vcavichini), [@xz-dev](https://github.com/xz-dev), [@zichen0116](https://github.com/zichen0116). +[@aliceisjustplaying](https://github.com/aliceisjustplaying), [@allenliang2022](https://github.com/allenliang2022), [@andrewkangkr](https://github.com/andrewkangkr), [@Carry00](https://github.com/Carry00), [@eleboucher](https://github.com/eleboucher), [@insecurejezza](https://github.com/insecurejezza), [@junjunjunbong](https://github.com/junjunjunbong), [@linuxid10t](https://github.com/linuxid10t), [@michael-dg](https://github.com/michael-dg), [@mmartial](https://github.com/mmartial), [@MrFant](https://github.com/MrFant), [@plerohellec](https://github.com/plerohellec), [@renatomott](https://github.com/renatomott), [@swftwolfzyq](https://github.com/swftwolfzyq), [@vcavichini](https://github.com/vcavichini), [@xz-dev](https://github.com/xz-dev), [@zichen0116](https://github.com/zichen0116). -## Single-PR contributors (84) +## Single-PR contributors (136) Each of these folks landed exactly one PR that shipped — a bug fix, a locale, a security hardening, a doc improvement, an infrastructure tweak. Every one moved the project forward. -[@29n](https://github.com/29n), [@amlyczz](https://github.com/amlyczz), [@andrewy-wizard](https://github.com/andrewy-wizard), [@Argonaut790](https://github.com/Argonaut790), [@Asunfly](https://github.com/Asunfly), [@ayushere](https://github.com/ayushere), [@betamod](https://github.com/betamod), [@Bobby9228](https://github.com/Bobby9228), [@bschmidy10](https://github.com/bschmidy10), [@carlytwozero](https://github.com/carlytwozero), [@davidsben](https://github.com/davidsben), [@DavidSchuchert](https://github.com/DavidSchuchert), [@DelightRun](https://github.com/DelightRun), [@DrMaks22](https://github.com/DrMaks22), [@eba8](https://github.com/eba8), [@eov128](https://github.com/eov128), [@galvani](https://github.com/galvani), [@GeoffBao](https://github.com/GeoffBao), [@georgebdavis](https://github.com/georgebdavis), [@GiggleSamurai](https://github.com/GiggleSamurai), [@hacker2005](https://github.com/hacker2005), [@halmisen](https://github.com/halmisen), [@hermes-gimmethebeans](https://github.com/hermes-gimmethebeans), [@hi-friday](https://github.com/hi-friday), [@huangzt](https://github.com/huangzt), [@indigokarasu](https://github.com/indigokarasu), [@insecurejezza](https://github.com/insecurejezza), [@jeffscottward](https://github.com/jeffscottward), [@Jellypowered](https://github.com/Jellypowered), [@jimdawdy-hub](https://github.com/jimdawdy-hub), [@JinYue-GitHub](https://github.com/JinYue-GitHub), [@joaompfp](https://github.com/joaompfp), [@jundev0001](https://github.com/jundev0001), [@KayZz69](https://github.com/KayZz69), [@kcclaw001](https://github.com/kcclaw001), [@kevin-ho](https://github.com/kevin-ho), [@koshikai](https://github.com/koshikai), [@kowenhaoai](https://github.com/kowenhaoai), [@lawrencel1ng](https://github.com/lawrencel1ng), [@legeantbleu](https://github.com/legeantbleu), [@likawa3b](https://github.com/likawa3b), [@lucky-yonug](https://github.com/lucky-yonug), [@lx3133584](https://github.com/lx3133584), [@MacLeodMike](https://github.com/MacLeodMike), [@mangodxd](https://github.com/mangodxd), [@mariosam95](https://github.com/mariosam95), [@MatzAgent](https://github.com/MatzAgent), [@mbac](https://github.com/mbac), [@migueltavares](https://github.com/migueltavares), [@mittyok](https://github.com/mittyok), [@ng-technology-llc](https://github.com/ng-technology-llc), [@octo-patch](https://github.com/octo-patch), [@plerohellec](https://github.com/plerohellec), [@rhelmer](https://github.com/rhelmer), [@rickchew](https://github.com/rickchew), [@ryan-remeo](https://github.com/ryan-remeo), [@ryansombraio](https://github.com/ryansombraio), [@s905060](https://github.com/s905060), [@SaulgoodMan-C](https://github.com/SaulgoodMan-C), [@sbe27](https://github.com/sbe27), [@shaoxianbilly](https://github.com/shaoxianbilly), [@sheng-di](https://github.com/sheng-di), [@sixianli](https://github.com/sixianli), [@skspade](https://github.com/skspade), [@smurmann](https://github.com/smurmann), [@snuffxxx](https://github.com/snuffxxx), [@spektro33](https://github.com/spektro33), [@Stampede](https://github.com/Stampede), [@suinia](https://github.com/suinia), [@sunnysktsang](https://github.com/sunnysktsang), [@tgaalman](https://github.com/tgaalman), [@thadreber-web](https://github.com/thadreber-web), [@the-own-lab](https://github.com/the-own-lab), [@tomaioo](https://github.com/tomaioo), [@trucuit](https://github.com/trucuit), [@vCillusion](https://github.com/vCillusion), [@vikarag](https://github.com/vikarag), [@wali-reheman](https://github.com/wali-reheman), [@watzon](https://github.com/watzon), [@woaijiadanoo](https://github.com/woaijiadanoo), [@xingyue52077](https://github.com/xingyue52077), [@yunyunyunyun-yun](https://github.com/yunyunyunyun-yun), [@yzp12138](https://github.com/yzp12138), [@zenc-cp](https://github.com/zenc-cp). +[@29n](https://github.com/29n), [@86cloudyun-afk](https://github.com/86cloudyun-afk), [@AdoneyGalvan](https://github.com/AdoneyGalvan), [@amlyczz](https://github.com/amlyczz), [@andrewy-wizard](https://github.com/andrewy-wizard), [@Argonaut790](https://github.com/Argonaut790), [@arshkumarsingh](https://github.com/arshkumarsingh), [@ashbuildslife](https://github.com/ashbuildslife), [@Asunfly](https://github.com/Asunfly), [@ayushere](https://github.com/ayushere), [@bengdan](https://github.com/bengdan), [@betamod](https://github.com/betamod), [@bjb2](https://github.com/bjb2), [@Bobby9228](https://github.com/Bobby9228), [@bschmidy10](https://github.com/bschmidy10), [@carlytwozero](https://github.com/carlytwozero), [@Charanis](https://github.com/Charanis), [@ChaseFlorell](https://github.com/ChaseFlorell), [@chwps](https://github.com/chwps), [@colin-chang](https://github.com/colin-chang), [@cyberdyne187](https://github.com/cyberdyne187), [@darkopetrovic](https://github.com/darkopetrovic), [@davidsben](https://github.com/davidsben), [@DavidSchuchert](https://github.com/DavidSchuchert), [@DelightRun](https://github.com/DelightRun), [@dev-rehaann](https://github.com/dev-rehaann), [@dotBeeps](https://github.com/dotBeeps), [@DrMaks22](https://github.com/DrMaks22), [@eba8](https://github.com/eba8), [@emanon312](https://github.com/emanon312), [@eov128](https://github.com/eov128), [@Fail-Safe](https://github.com/Fail-Safe), [@FrancescoFarinola](https://github.com/FrancescoFarinola), [@gabogabucho](https://github.com/gabogabucho), [@galvani](https://github.com/galvani), [@gavinssr](https://github.com/gavinssr), [@GeoffBao](https://github.com/GeoffBao), [@georgebdavis](https://github.com/georgebdavis), [@GiggleSamurai](https://github.com/GiggleSamurai), [@hacker1e7](https://github.com/hacker1e7), [@hacker2005](https://github.com/hacker2005), [@halmisen](https://github.com/halmisen), [@hermes-gimmethebeans](https://github.com/hermes-gimmethebeans), [@hi-friday](https://github.com/hi-friday), [@hualong1009](https://github.com/hualong1009), [@huangzt](https://github.com/huangzt), [@indigokarasu](https://github.com/indigokarasu), [@intellectronica](https://github.com/intellectronica), [@jeffscottward](https://github.com/jeffscottward), [@Jellypowered](https://github.com/Jellypowered), [@jimdawdy-hub](https://github.com/jimdawdy-hub), [@JinYue-GitHub](https://github.com/JinYue-GitHub), [@joaompfp](https://github.com/joaompfp), [@jundev0001](https://github.com/jundev0001), [@KayZz69](https://github.com/KayZz69), [@kcclaw001](https://github.com/kcclaw001), [@kevin-ho](https://github.com/kevin-ho), [@koshikai](https://github.com/koshikai), [@kowenhaoai](https://github.com/kowenhaoai), [@lawrencel1ng](https://github.com/lawrencel1ng), [@leap233](https://github.com/leap233), [@legeantbleu](https://github.com/legeantbleu), [@likawa3b](https://github.com/likawa3b), [@lost9999](https://github.com/lost9999), [@lucky-yonug](https://github.com/lucky-yonug), [@lx3133584](https://github.com/lx3133584), [@MacLeodMike](https://github.com/MacLeodMike), [@malulian](https://github.com/malulian), [@mangodxd](https://github.com/mangodxd), [@mariosam95](https://github.com/mariosam95), [@MatzAgent](https://github.com/MatzAgent), [@mbac](https://github.com/mbac), [@migueltavares](https://github.com/migueltavares), [@MinhoJJang](https://github.com/MinhoJJang), [@mittyok](https://github.com/mittyok), [@mslovy](https://github.com/mslovy), [@mvanhorn](https://github.com/mvanhorn), [@nanookclaw](https://github.com/nanookclaw), [@ng-technology-llc](https://github.com/ng-technology-llc), [@nickgiulioni1](https://github.com/nickgiulioni1), [@octo-patch](https://github.com/octo-patch), [@OneFat3](https://github.com/OneFat3), [@PINKIIILQWQ](https://github.com/PINKIIILQWQ), [@rhelmer](https://github.com/rhelmer), [@rickchew](https://github.com/rickchew), [@RobertoVillegas](https://github.com/RobertoVillegas), [@ruxme](https://github.com/ruxme), [@ryan-remeo](https://github.com/ryan-remeo), [@ryansombraio](https://github.com/ryansombraio), [@s905060](https://github.com/s905060), [@Saik0s](https://github.com/Saik0s), [@samuelgudi](https://github.com/samuelgudi), [@SaulgoodMan-C](https://github.com/SaulgoodMan-C), [@sbe27](https://github.com/sbe27), [@shaoxianbilly](https://github.com/shaoxianbilly), [@sheng-di](https://github.com/sheng-di), [@shruggr](https://github.com/shruggr), [@sixianli](https://github.com/sixianli), [@skspade](https://github.com/skspade), [@smurmann](https://github.com/smurmann), [@snuffxxx](https://github.com/snuffxxx), [@someaka](https://github.com/someaka), [@spektro33](https://github.com/spektro33), [@Stampede](https://github.com/Stampede), [@stocky789](https://github.com/stocky789), [@suinia](https://github.com/suinia), [@sunilkumarvalmiki](https://github.com/sunilkumarvalmiki), [@sunnysktsang](https://github.com/sunnysktsang), [@TaraTheStar](https://github.com/TaraTheStar), [@tgaalman](https://github.com/tgaalman), [@thadreber-web](https://github.com/thadreber-web), [@the-own-lab](https://github.com/the-own-lab), [@theh4v0c](https://github.com/theh4v0c), [@theseussss](https://github.com/theseussss), [@tiansiyuan](https://github.com/tiansiyuan), [@tomaioo](https://github.com/tomaioo), [@trucuit](https://github.com/trucuit), [@ts2111](https://github.com/ts2111), [@v2psv](https://github.com/v2psv), [@vansour](https://github.com/vansour), [@vCillusion](https://github.com/vCillusion), [@vikarag](https://github.com/vikarag), [@waldmanz](https://github.com/waldmanz), [@wali-reheman](https://github.com/wali-reheman), [@watzon](https://github.com/watzon), [@weidzhou](https://github.com/weidzhou), [@weiwei83](https://github.com/weiwei83), [@wind-chant](https://github.com/wind-chant), [@wirtsi](https://github.com/wirtsi), [@woaijiadanoo](https://github.com/woaijiadanoo), [@xingyue52077](https://github.com/xingyue52077), [@xolom](https://github.com/xolom), [@yunyunyunyun-yun](https://github.com/yunyunyunyun-yun), [@yzp12138](https://github.com/yzp12138), [@zapabob](https://github.com/zapabob), [@zenc-cp](https://github.com/zenc-cp). --- @@ -82,12 +89,12 @@ All four paths count as a contribution. GitHub's `merged_at` field only catches ## Special thanks - **[@aronprins](https://github.com/aronprins)** — `v0.50.0` UI overhaul (PR #242). The CSS-only redesign that defined the design tokens, theme architecture, and three-panel layout that the rest of the app builds on. PR #242 didn't merge as-is, but it is the design language of the app. -- **[@franksong2702](https://github.com/franksong2702)** — most prolific external contributor across the project's history. 117 PRs spanning the session sidebar, mobile/responsive layout, workspace state machine, profile context, slash autocomplete, breadcrumb navigation, streaming-session exemption, cron output preservation, embedded terminal, the manual-compress async start/status endpoint pair, the worktree status surface (PR #2109) + guarded remove (PR #2156) for the lifecycle umbrella #2057, session post-render dedup (PR #2166), the `/api/session` native-WebUI fast path (PR #2170), tail-window response trim (PR #2171), the second-wave stale-stream guard extension (PR #2158), CSP report collector (PR #2160), and the long tail of polish. -- **[@Michaelyklam](https://github.com/Michaelyklam)** — most prolific contributor of late-2025/early-2026. 92 PRs covering Docker hardening, profile-scoped skills, KaTeX delimiter parsing, Codex quota surfacing, Goal command, Kanban polish, auto-compression toast lifetime, the localization parity backfills, the v0.51.51 mobile Insights bucketing/layout pair (PRs #2120/#2121), the Hermes run adapter RFC (PR #2105 for #1925), the fork-from-here absolute-index fix (PR #2198 for #2184), and the opencode-go custom-provider overlap routing fix (PR #2204 for #1894). -- **[@bergeouss](https://github.com/bergeouss)** — provider-management UI, OAuth status, two-container Docker docs, profile isolation hardening, Reveal-in-Finder, the OpenRouter free-tier live fetch, and most of Settings → Providers. 62 PRs. -- **[@ai-ag2026](https://github.com/ai-ag2026)** — autonomous-AI contributor (Hermes Agent-driven). 55 PRs focused on session recovery (state.db sidecar reconciliation, orphan `.bak` recovery, audit + safe-repair endpoints), workspace/run lifecycle health, the crash-safe turn-journal RFC, the append-only turn-journal helper (PR #2059), the matching lifecycle-events layer (PR #2062), the `Content-Security-Policy-Report-Only` header (PR #2084), and the per-cron toast notification toggle (PR #2100). +- **[@franksong2702](https://github.com/franksong2702)** — most prolific external contributor across the project's history. 148 PRs spanning the session sidebar, mobile/responsive layout, workspace state machine, profile context, slash autocomplete, breadcrumb navigation, streaming-session exemption, cron output preservation, embedded terminal, the manual-compress async start/status endpoint pair, the worktree status surface (PR #2109) + guarded remove (PR #2156) for the lifecycle umbrella #2057, session post-render dedup (PR #2166), the `/api/session` native-WebUI fast path (PR #2170), tail-window response trim (PR #2171), the second-wave stale-stream guard extension (PR #2158), CSP report collector (PR #2160), and the long tail of polish. +- **[@Michaelyklam](https://github.com/Michaelyklam)** — most prolific contributor of late-2025/early-2026. 117 PRs covering Docker hardening, profile-scoped skills, KaTeX delimiter parsing, Codex quota surfacing, Goal command, Kanban polish, auto-compression toast lifetime, the localization parity backfills, the v0.51.51 mobile Insights bucketing/layout pair (PRs #2120/#2121), the Hermes run adapter RFC (PR #2105 for #1925), the fork-from-here absolute-index fix (PR #2198 for #2184), and the opencode-go custom-provider overlap routing fix (PR #2204 for #1894). +- **[@bergeouss](https://github.com/bergeouss)** — provider-management UI, OAuth status, two-container Docker docs, profile isolation hardening, Reveal-in-Finder, the OpenRouter free-tier live fetch, and most of Settings → Providers. 70 PRs. +- **[@ai-ag2026](https://github.com/ai-ag2026)** — autonomous-AI contributor (Hermes Agent-driven). 67 PRs focused on session recovery (state.db sidecar reconciliation, orphan `.bak` recovery, audit + safe-repair endpoints), workspace/run lifecycle health, the crash-safe turn-journal RFC, the append-only turn-journal helper (PR #2059), the matching lifecycle-events layer (PR #2062), the `Content-Security-Policy-Report-Only` header (PR #2084), and the per-cron toast notification toggle (PR #2100). - **[@Jordan-SkyLF](https://github.com/Jordan-SkyLF)** — Live streaming, session recovery, workspace fallback, and a recent burst of presentation polish: the manual "Refresh usage" button on the Provider quota card (PR #2150), cancelled-turn status classification (PR #2151), Firefox sidebar scroll stabilization (PR #2200), early provisional session titles (PR #2202), target-aware "What's new?" links (PR #2207), and MCP tools overflow fix in Settings (PR #2210). 12 PRs total. -- **[@LumenYoung](https://github.com/LumenYoung)** — newer contributor focused on the streaming hot path's correctness. 4 PRs including the original stale-stream writeback guard (PR #2136 — the bug class the next two releases extended), gateway-state alive-null classification (PR #2075), compression-banner anchor alignment (PR #2182), and context-progress ring auto-refresh on compression complete (PR #2188). +- **[@LumenYoung](https://github.com/LumenYoung)** — contributor focused on the streaming hot path's correctness. 8 PRs including the original stale-stream writeback guard (PR #2136 — the bug class the next two releases extended), gateway-state alive-null classification (PR #2075), compression-banner anchor alignment (PR #2182), and context-progress ring auto-refresh on compression complete (PR #2188). - **[@iRonin](https://github.com/iRonin)** — security hardening sprint (PRs #196–#204): session memory leak fix, CSP + Permissions-Policy headers, slow-client connection timeout, optional HTTPS/TLS, upstream branch tracking, CLI session file-browser support. Six consecutive, focused, high-quality security PRs. - **[@indigokarasu](https://github.com/indigokarasu)** — visual redesign proposal (PR #213). Icon rail sidebar, design token system, 7 themes. Didn't merge as-is but shaped the design language that landed in v0.50.0. - **[@zenc-cp](https://github.com/zenc-cp)** — anti-hallucination guard for the ReAct loop (PR #133). Three-layer approach (ephemeral prompt, live token filtering, session-history cleanup) that the streaming pipeline still uses. diff --git a/README.md b/README.md index 447709a3f..d0e51ab9b 100644 --- a/README.md +++ b/README.md @@ -712,7 +712,7 @@ Full design notes and the endpoint catalog are in [`ARCHITECTURE.md`](ARCHITECTU Hermes WebUI is built with help from the open-source community. Every PR — whether merged directly, absorbed into a batch release, or salvaged from a larger proposal — shapes the project, and we're grateful to everyone who has taken the time to contribute. -Over **137 contributors** have shipped code that landed in a release tag. The full, +Over **190 contributors** have shipped code that landed in a release tag. The full, continuously-updated credit roll — including everyone with one or two PRs and the special-thanks roll for design and architectural work — lives in [`CONTRIBUTORS.md`](CONTRIBUTORS.md). A snapshot of the most prolific contributors: @@ -721,18 +721,18 @@ special-thanks roll for design and architectural work — lives in | # | Contributor | PRs | First → latest release | |---|---|---:|---| -| 1 | [@franksong2702](https://github.com/franksong2702) | 117 | `v0.49.3` → `v0.51.58` | -| 2 | [@Michaelyklam](https://github.com/Michaelyklam) | 92 | `v0.50.240` → `v0.51.57` | -| 3 | [@bergeouss](https://github.com/bergeouss) | 62 | `v0.48.0` → `v0.51.46` | -| 4 | [@ai-ag2026](https://github.com/ai-ag2026) | 55 | `v0.50.279` → `v0.51.47` | -| 5 | [@dso2ng](https://github.com/dso2ng) | 23 | `v0.50.227` → `v0.51.51` | -| 6 | [@jasonjcwu](https://github.com/jasonjcwu) | 16 | `v0.50.227` → `v0.51.55` | -| 7 | [@Jordan-SkyLF](https://github.com/Jordan-SkyLF) | 12 | `v0.50.18` → `v0.51.58` | -| 8 | [@aronprins](https://github.com/aronprins) | 10 | `v0.44.0` → `v0.50.233` | -| 9 | [@JKJameson](https://github.com/JKJameson) | 10 | `v0.50.233` → `v0.51.31` | -| 10 | [@starship-s](https://github.com/starship-s) | 10 | `v0.50.128` → `v0.51.58` | +| 1 | [@franksong2702](https://github.com/franksong2702) | 148 | `v0.49.3` → `v0.51.153` | +| 2 | [@Michaelyklam](https://github.com/Michaelyklam) | 117 | `v0.50.240` → `v0.51.139` | +| 3 | [@bergeouss](https://github.com/bergeouss) | 70 | `v0.48.0` → `v0.51.46` | +| 4 | [@ai-ag2026](https://github.com/ai-ag2026) | 67 | `v0.50.279` → `v0.51.190` | +| 5 | [@dso2ng](https://github.com/dso2ng) | 25 | `v0.50.227` → `v0.51.153` | +| 6 | [@AJV20](https://github.com/AJV20) | 24 | `v0.51.93` → `v0.51.188` | +| 7 | [@starship-s](https://github.com/starship-s) | 19 | `v0.50.123` → `v0.51.153` | +| 8 | [@jasonjcwu](https://github.com/jasonjcwu) | 16 | `v0.50.227` → `v0.51.132` | +| 9 | [@dobby-d-elf](https://github.com/dobby-d-elf) | 15 | `v0.51.38` → `v0.51.161` | +| 10 | [@Jordan-SkyLF](https://github.com/Jordan-SkyLF) | 12 | `v0.50.18` → `v0.51.66` | -See [`CONTRIBUTORS.md`](CONTRIBUTORS.md) for the full ranked list of all 137 contributors, including everyone with one or two PRs and the special-thanks roll for design and architectural contributions. +See [`CONTRIBUTORS.md`](CONTRIBUTORS.md) for the full ranked list of all 194 contributors, including everyone with one or two PRs and the special-thanks roll for design and architectural contributions. ### Notable contributions From 9d1b7b1d5298a43529a0da1f95e3e6cba0898fea Mon Sep 17 00:00:00 2001 From: nesquena-hermes <[email protected]> Date: Mon, 1 Jun 2026 02:16:57 +0000 Subject: [PATCH 3/5] docs: freshen ROADMAP + ARCHITECTURE content + README notable-contrib counts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ROADMAP.md: 8-themes -> 11-skins in status snapshot; replace stale per-file LOC architecture table with role descriptions + CI gate list (ruff/shard/ browser/docker); note LOC drifts. - ARCHITECTURE.md: refresh File Inventory — drop chronically-stale per-file LOC, add missing modules (state_sync.py, updates.py, i18n/login/icons/sw.js, pyproject.toml, ctl.sh, CONTRIBUTORS.md), refresh test count to ~7,150. - README.md: update the 8 'Notable contributions' blurb headers (PR counts + release ranges) to match the regenerated tally. --- ARCHITECTURE.md | 70 +++++++++++++++++++++++++++++-------------------- README.md | 18 ++++++------- ROADMAP.md | 14 +++++----- 3 files changed, 58 insertions(+), 44 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 8f6e5abbb..7e586f2c1 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -45,51 +45,63 @@ actions. The topbar remains focused on conversation context and the workspace/fi ## 2. File Inventory / - server.py Thin routing shell + HTTP Handler + auth middleware. ~446 lines. + server.py Thin routing shell + HTTP Handler + auth middleware. Delegates all route handling to api/routes.py. bootstrap.py One-shot launcher: optional agent install, deps, health wait, browser open. start.sh Thin wrapper around bootstrap.py for shell-based startup. - Dockerfile python:3.12-slim container image (~89 lines) - docker-compose.yml Compose config with named volume and optional auth (~57 lines) + ctl.sh Daemon lifecycle wrapper (start/stop/restart/status/logs) for homelab installs. + pyproject.toml Tooling config (ruff lint gate). NOT a packaged distribution. + Dockerfile python:3.12-slim container image + docker-compose.yml Compose config with named volume and optional auth .dockerignore Excludes .git, tests/, .env* from Docker builds api/ __init__.py Package marker - auth.py Optional password authentication, signed cookies (~366 lines) - config.py Discovery, globals, model detection, reloadable config (~4139 lines) - helpers.py HTTP helpers: j(), bad(), require(), safe_resolve(), security headers (~302 lines) - models.py Session model + CRUD, per-session profile tracking (~1927 lines) - profiles.py Profile state management, hermes_cli wrapper (~1056 lines) - onboarding.py First-run onboarding status, real provider config writes, OAuth linking, and readiness detection (~1002 lines) - routes.py All GET + POST route handlers (~9772 lines) - startup.py Startup helpers: auto_install_agent_deps() (~128 lines) - streaming.py SSE engine, run_agent, cancel, HERMES_HOME save/restore (~4420 lines) - upload.py Multipart parser, file upload handler (~284 lines) - workspace.py File ops: list_dir, read_file_content, workspace helpers (~810 lines) + auth.py Optional password authentication, signed cookies, passkeys/WebAuthn + config.py Discovery, globals, model detection, reloadable config + helpers.py HTTP helpers: j(), bad(), require(), safe_resolve(), security headers + models.py Session model + CRUD, per-session profile tracking, CLI/state.db bridge + profiles.py Profile state management, hermes_cli wrapper + onboarding.py First-run onboarding status, real provider config writes, OAuth linking, readiness detection + routes.py All GET + POST route handlers (if/elif dispatch, no decorators) + startup.py Startup helpers: auto_install_agent_deps() + state_sync.py /insights sync — message_count to the agent's state.db + streaming.py SSE engine, run_agent, cancel, compression, HERMES_HOME save/restore + updates.py Self-update check and release notes + upload.py Multipart parser, file upload handler + workspace.py File ops: list_dir, read_file_content, git detection, workspace helpers static/ - index.html HTML template (~1323 lines) - style.css All CSS incl. mobile responsive (~3767 lines) - ui.js DOM helpers, renderMd, tool cards, model dropdown, file tree (~7216 lines) - workspace.js File preview, file ops, loadDir, clearPreview (~369 lines) - sessions.js Session CRUD, list rendering, search, SVG icons, dropdown actions (~3517 lines) - messages.js send(), SSE event handlers, approval, transcript (~2301 lines) - panels.js Cron, skills, memory, workspace, profiles, todo, settings (~6480 lines) - commands.js Slash command registry, parser, autocomplete dropdown (~1302 lines) - onboarding.js First-run wizard overlay, provider setup flow, and settings/workspace orchestration. - boot.js Event wiring, mobile sidebar/workspace nav, voice input, boot IIFE (~1607 lines) + index.html HTML template + style.css All CSS incl. mobile responsive, themes + skins, KaTeX + ui.js DOM helpers, renderMd, tool cards, context indicator, file tree + workspace.js File preview, file ops, git badge, central api() fetch wrapper + sessions.js Session CRUD, list rendering, collapsible groups, search, SSE sync + messages.js send(), SSE event handlers, approval/clarify, transcript, recovery + panels.js Cron, skills, memory, profiles, todo, settings (Control Center) + commands.js Slash command registry, parser, autocomplete dropdown + boot.js Event wiring, mobile nav, voice input, theme/skin boot, bfcache handler + onboarding.js First-run wizard overlay, provider setup flow + i18n.js Localization catalog (en, es, de, zh, zh-Hant, ru, …) + login.js Login page + open-redirect guard + icons.js Lucide icon path registry + sw.js Service worker: offline shell cache, version-pinned assets tests/ - conftest.py Isolated test server/state fixtures (~644 lines) - 488 test files 5303 tests collected via pytest - test_regressions.py Permanent regression gate (~976 lines) + conftest.py Isolated test server/state fixtures + ~700 test files ~7,150 tests collected via pytest (run `pytest --collect-only -q` for exact) + test_regressions.py Permanent regression gate CONTRIBUTING.md Contributor workflow and PR expectations. ROADMAP.md Feature and product roadmap document. SPRINTS.md Forward sprint plan with CLI + Claude parity targets. ARCHITECTURE.md THIS FILE. TESTING.md Manual browser test plan and automated coverage reference. - CHANGELOG.md Release notes per sprint. - BUGS.md Bug backlog and fixed items tracker. + CHANGELOG.md Release notes per version. + CONTRIBUTORS.md Community credit roll (regenerated via the maintainer workspace script). requirements.txt Python dependencies. .env.example Sample environment variable overrides. +> Per-file line counts intentionally omitted — they drift every release. Use +> `git ls-files | xargs wc -l` (or your editor) for current sizes; the role of +> each file above is the durable part. + State directory (runtime data, separate from source): ~/.hermes/webui/ diff --git a/README.md b/README.md index d0e51ab9b..c1370afe3 100644 --- a/README.md +++ b/README.md @@ -736,25 +736,25 @@ See [`CONTRIBUTORS.md`](CONTRIBUTORS.md) for the full ranked list of all 194 con ### Notable contributions -**[@franksong2702](https://github.com/franksong2702)** — Most prolific external contributor (117 PRs, `v0.49.3` → `v0.51.58`) +**[@franksong2702](https://github.com/franksong2702)** — Most prolific external contributor (148 PRs, `v0.49.3` → `v0.51.153`) Across the longest tenure of any external contributor: the session title guard (#301), breadcrumb workspace navigation (#302), embedded workspace terminal (#1099), worktree-backed session creation (#2053), onboarding documentation (#2052), composer footer container queries, streaming-session sidebar exemption (#1327), session sidecar repair, cron output preservation (#1295), profile default workspace persistence, manual `/compress` async start/status endpoints (#2128), worktree status surface (#2109) + guarded remove (#2156) for the lifecycle umbrella #2057, session post-render dedup (#2166), native-WebUI fast path (#2170), tail-window response trim (#2171), stale-stream guard extension (#2158), CSP report collector (#2160), and a long tail of polish across mobile/responsive, the session sidebar, and the workspace state machine. -**[@Michaelyklam](https://github.com/Michaelyklam)** — Most prolific contributor of recent releases (92 PRs, `v0.50.240` → `v0.51.57`) +**[@Michaelyklam](https://github.com/Michaelyklam)** — Most prolific contributor of recent releases (117 PRs, `v0.50.240` → `v0.51.139`) Production Docker hardening (#1921, drops sudo-capable staging user), profile-scoped skills endpoints (#1903), gateway PID resolution under profile-scoped HERMES_HOME (#1901), profile-aware AIAgent cache (#1898/#1904), backslash LaTeX delimiters (#1848), Codex quota error surfacing (#1770), shell-route HTML 503 (#1836), stale Kanban client recovery (#1828), context auto-compression toast lifetime (#1988), `/goal` command (#1866), Kanban detail-view scrolling (#1916), CLI session tool metadata preservation (#1778), Traditional Chinese kanban locale backfill (#1979), v0.51.51 mobile Insights bucketing/layout (#2120/#2121), Hermes run adapter RFC (#2105 for #1925), fork-from-here absolute index (#2198 for #2184), opencode-go custom-provider overlap routing (#2204 for #1894). -**[@bergeouss](https://github.com/bergeouss)** — Provider management UI + Docker hardening (62 PRs, `v0.48.0` → `v0.51.46`) +**[@bergeouss](https://github.com/bergeouss)** — Provider management UI + Docker hardening (70 PRs, `v0.48.0` → `v0.51.46`) Provider management UI for adding/editing custom providers from Settings, OAuth provider status detection (#1552), two-container Docker setup, profile isolation hardening (per-profile `.env` secrets), the bulk of what users see when they touch Settings → Providers, Reveal-in-Finder context menu (#1551), gateway status card (#1552), auto-assign session to active project filter (#1550), "What's new?" link in update banner (#1549), OpenRouter free-tier live fetch (#1548), credential pool 401 self-heal (#1553), inline provider chip + group model count in model picker (#1644). -**[@ai-ag2026](https://github.com/ai-ag2026)** — Session recovery + audit infrastructure (55 PRs, `v0.50.279` → `v0.51.47`) +**[@ai-ag2026](https://github.com/ai-ag2026)** — Session recovery + audit infrastructure (67 PRs, `v0.50.279` → `v0.51.190`) Autonomous-AI contributor (Hermes Agent-driven) focused on durability: `state.db`-backed sidecar reconciliation (#2041), orphan `.json.bak` recovery on startup (#2035), read-only session recovery audit endpoints (#2036, #2040), active run lifecycle in `/health` (#2039), crash-safe turn-journal RFC at `docs/rfcs/turn-journal.md` (#2042), append-only turn-journal helper (#2059), lifecycle events layer (#2062), `Content-Security-Policy-Report-Only` header (#2084), per-cron toast toggle (#2100), fork-session compression lineage isolation (#2014). -**[@dso2ng](https://github.com/dso2ng)** — Session lineage + diagnostics (23 PRs, `v0.50.227` → `v0.51.51`) +**[@dso2ng](https://github.com/dso2ng)** — Session lineage + diagnostics (25 PRs, `v0.50.227` → `v0.51.153`) `/api/session/lineage-report/` endpoint for bounded session graph diagnostics (#2012), stale Mermaid render error cleanup (#1337), `session_source="fork"` continuation-chain isolation (#2063), lazy lineage-report fetch on sidebar badge expand (#2130), and a long tail of frontend reliability fixes around session loading. -**[@jasonjcwu](https://github.com/jasonjcwu)** — Composer + transcript polish (16 PRs, `v0.50.227` → `v0.51.55`) +**[@jasonjcwu](https://github.com/jasonjcwu)** — Composer + transcript polish (16 PRs, `v0.50.227` → `v0.51.132`) Sidebar collapse via active-rail click (#2054, fuses #1884 + #1924), composer chip lightbox (#1758), title fixes for tool-heavy first turns, silent compress-status during session switch (#2185), concurrent-send loss fix (#2186), in-transcript steer message badges (#2187), and a string of frontend polish fixes. -**[@Jordan-SkyLF](https://github.com/Jordan-SkyLF)** — Live streaming + UX polish (12 PRs, `v0.50.18` → `v0.51.58`) +**[@Jordan-SkyLF](https://github.com/Jordan-SkyLF)** — Live streaming + UX polish (12 PRs, `v0.50.18` → `v0.51.66`) Original sprint of workspace fallback resolution, live reasoning cards (#366, #367, #394–#397), then a recent burst: manual "Refresh usage" button on the Provider quota card (#2150), cancelled-turn status classification (#2151), Firefox sidebar scroll stabilization (#2200), early provisional session titles (#2202), target-aware "What's new?" update-banner links (#2207), and MCP tools overflow fix in Settings (#2210). **[@aronprins](https://github.com/aronprins)** — `v0.50.0` UI overhaul (PR #242, plus 9 follow-ups) @@ -766,10 +766,10 @@ Six consecutive, focused security PRs: session memory leak fix (expired token pr **[@lucasrc](https://github.com/lucasrc)** — Auth-hardening trilogy (PRs #2191, #2192, #2193) Three coordinated security PRs that all landed in v0.51.57: thread-safe login rate limiter with PBKDF2 key separation, password-hash cache invalidation on Settings save, and the full 64-char HMAC-SHA256 session signature with a backwards-compatible migration bridge. The kind of cleanly-decomposed security work that's reviewable as three independent pieces. -**[@LumenYoung](https://github.com/LumenYoung)** — Streaming hot-path correctness (4 PRs, `v0.51.47` → `v0.51.55`) +**[@LumenYoung](https://github.com/LumenYoung)** — Streaming hot-path correctness (8 PRs, `v0.51.47` → `v0.51.99`) The original stale-stream writeback guard (#2136 — the bug class the next two releases extended), gateway-state alive-null classification (#2075), compression-banner anchor alignment (#2182), and context-progress ring auto-refresh on compression complete (#2188). Each PR opened a small surgical fix in one of the most fragile subsystems in the codebase. -**[@dobby-d-elf](https://github.com/dobby-d-elf)** — Frontend reliability + motion polish (6 PRs, `v0.51.38` → `v0.51.58`) +**[@dobby-d-elf](https://github.com/dobby-d-elf)** — Frontend reliability + motion polish (15 PRs, `v0.51.38` → `v0.51.161`) Workspace fallback on deleted directories (#2138), iPhone PWA bottom-scroll fix (#2143), the new "Activity: X tools" composer footer shimmer animation (#2203), and follow-up animation tuning (#2212). **[@JKJameson](https://github.com/JKJameson)** — Composer + session polish (10 PRs) diff --git a/ROADMAP.md b/ROADMAP.md index 4aceb531a..8a26fbe67 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -18,7 +18,7 @@ | **Sessions + projects + search** | ✅ CRUD, content search, projects, tags, archive, fork, import | | **Mobile + Docker + auth** | ✅ Hamburger nav, slide-overs, password auth, GHCR images | | **Auxiliary surfaces** | ✅ Workspace tree + edit, cron CRUD, skills CRUD, memory write, MCP server UI | -| **Visual polish** | ✅ 8 themes (incl. light/system/OLED/Sienna), Mermaid, KaTeX, syntax highlighting | +| **Visual polish** | ✅ Light/dark/system themes × 11 skins (default, ares, mono, slate, poseidon, sisyphus, charizard, sienna, catppuccin, nous, geist-contrast), Mermaid, KaTeX, syntax highlighting | | **Native distribution** | ✅ macOS desktop app (universal arm64+x86_64 DMG, signed) — separate repo | Remaining gaps and forward work live in [Forward Work](#forward-work) below. @@ -29,15 +29,17 @@ Remaining gaps and forward work live in [Forward Work](#forward-work) below. | Layer | Files | Status | |---|---|---| -| Python server | `server.py` (~165 lines) + `api/` modules (~20k lines) | Thin shell + auth middleware + business logic | -| HTML template | `static/index.html` (~600 lines) | Served from disk | -| CSS | `static/style.css` (~3k lines) | Themes, mobile responsive, KaTeX, table styles | -| JavaScript | `static/{ui,sessions,messages,workspace,panels,boot,commands,icons,i18n,login,onboarding}.js` (~26k lines) | 11 modules served as static files | +| Python server | `server.py` + `api/` modules | Thin HTTP shell + auth middleware over the `api/` business logic (config, sessions, streaming, profiles, routes, onboarding, workspace, updates, upload) | +| HTML template | `static/index.html` | Served from disk | +| CSS | `static/style.css` | Themes + skins, mobile responsive, KaTeX, table styles | +| JavaScript | `static/{ui,sessions,messages,workspace,panels,boot,commands,icons,i18n,login,onboarding}.js` | Vanilla-JS modules served as static files — no bundler | | Service worker | `static/sw.js` | Offline shell cache, version-pinned assets | | Docker | `Dockerfile`, `docker-compose.yml` | `python:3.12-slim`, multi-arch (amd64+arm64), HEALTHCHECK | -| CI/CD | `.github/workflows/release.yml` | Auto-release + GHCR publish on tag push | +| CI/CD | `.github/workflows/` | ruff lint + sharded pytest + browser smoke + Docker smoke on every PR; auto-release + GHCR publish on tag push | | Test isolation | `tests/_pytest_port.py` | Per-worktree port + state-dir derivation, no collisions | +> Per-file line counts drift every release; see `ARCHITECTURE.md` for the current module map and `git ls-files` for exact sizes. + --- ## Feature parity checklist From ec168b3c67398cb1bb0a1ceea02c0246f169d054 Mon Sep 17 00:00:00 2001 From: nesquena-hermes <[email protected]> Date: Mon, 1 Jun 2026 02:26:18 +0000 Subject: [PATCH 4/5] =?UTF-8?q?docs(readme):=20re-sequence=20IA=20?= =?UTF-8?q?=E2=80=94=20pull=20Features=20up,=20consolidate=20access,=20ext?= =?UTF-8?q?ract=20niche=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Information-architecture pass on the 840-line README so the most important things come first and secondary/niche content is linked rather than inline: - Reorder: Why -> Quick start -> FEATURES (was at line 502, now right after Quick start) -> Configuration & access -> Docker -> Running tests -> Architecture -> Docs -> Contributors. Readers see what it does before the deployment minutiae. - Consolidate the scattered access sections (start.sh discovery, overrides, remote/SSH, Tailscale, manual launch) under one '## Configuration & access' H2 with H3 subsections. - Extract two genuinely-niche blocks to new linked docs (nothing deleted): - docs/advanced-chat-setup.md — dynamic recall-prefill + Gateway-backed chat - docs/remote-access.md — SSH tunnel + Tailscale + ARM64-Android field report Quick start keeps a one-line pointer to each. - Update Contents TOC + Docs index for the new order and new files. README 840 -> 705 lines; content preserved (verified moved-not-dropped); all internal links + new docs verified to resolve; docs/*.md gitignore-allowlisted. --- README.md | 546 ++++++++++++++---------------------- docs/advanced-chat-setup.md | 83 ++++++ docs/remote-access.md | 75 +++++ 3 files changed, 364 insertions(+), 340 deletions(-) create mode 100644 docs/advanced-chat-setup.md create mode 100644 docs/remote-access.md diff --git a/README.md b/README.md index c1370afe3..a036ecf3a 100644 --- a/README.md +++ b/README.md @@ -49,10 +49,10 @@ This gives you nearly **1:1 parity with Hermes CLI from a convenient web UI** wh - [Why Hermes](#why-hermes) — what it is and how it compares - [Quick start](#quick-start) — clone + `bootstrap.py` / `start.sh` / `ctl.sh` -- [Docker](#docker) — single- and multi-container deploys -- [Configuration & access](#what-startsh-discovers-automatically) — auto-discovery, overrides, remote/Tailscale/phone, manual launch -- [Running tests](#running-tests) - [Features](#features) — chat, sessions, workspace, voice, profiles, security, themes, panels, mobile +- [Configuration & access](#configuration--access) — auto-discovery, overrides, remote/Tailscale/phone, manual launch +- [Docker](#docker) — single- and multi-container deploys +- [Running tests](#running-tests) - [Architecture](#architecture) — backend/frontend layout, state dir - [Docs](#docs) — the full documentation index - [Contributors](#contributors) @@ -135,85 +135,9 @@ For self-hosted VM or homelab installs, `ctl.sh` wraps the common daemon lifecyc `ctl.sh start` runs the bootstrap in foreground/no-browser mode behind the daemon wrapper, writes logs to `~/.hermes/webui.log`, and respects `.env` plus inline overrides such as `HERMES_WEBUI_HOST=0.0.0.0 ./ctl.sh start`. -### Optional session recall prefill +### Advanced: dynamic recall prefill & Gateway-backed chat -WebUI can attach ephemeral prefill messages to new browser-originated -agent turns. This is useful when a deployment already has a local recall or -router script for Joplin, Obsidian, Notion, llm-wiki, or another third-party -notes source and wants browser chat to know where durable context lives. - -Prefer a compact router-style prefill (for example, "Joplin has the durable -project context; use the available notes/search tools before answering -detail-dependent questions") instead of dumping the full note corpus into every -new browser session. The prefill should point the agent toward retrieval; the -notes/search tools should provide the specific facts on demand. - -Static JSON remains supported through `prefill_messages_file` or -`HERMES_PREFILL_MESSAGES_FILE`. For dynamic recall, opt in explicitly with a -WebUI-specific script hook: - -```yaml -webui_prefill_messages_script: - - python3 - - /path/to/notes_recall.py -webui_prefill_messages_script_timeout: 5 -``` - -or: - -```bash -HERMES_WEBUI_PREFILL_MESSAGES_SCRIPT="python3 /path/to/notes_recall.py" \ -HERMES_WEBUI_PREFILL_MESSAGES_SCRIPT_TIMEOUT=5 \ -./ctl.sh restart -``` - -The script may print either an OpenAI-style JSON message list, a JSON object with -a `messages` list, or plain text; plain text is wrapped as one `user` prefill -message so dynamic recall text becomes ordinary context instead of an extra -system instruction. If the hook must provide system-level guidance, emit JSON -messages with an explicit `role: "system"` entry instead. Script output is capped -at 256 KiB before parsing. Parsed prefill context is then bounded by -`webui_prefill_context_max_chars` or `HERMES_WEBUI_PREFILL_CONTEXT_MAX_CHARS` -(default: 12,000 characters; set to `0` to disable). When a dynamic script -exceeds the budget and a compact static prefill file is configured, WebUI falls -back to that file. If no compact fallback is available, WebUI injects a short -retrieval instruction instead of sending the oversized note/body payload with -every new browser turn. The browser only receives a compact status event -(`source`, `label`, message count, compaction metadata, and redacted errors), -never the prefill message bodies. - -### Optional Gateway-backed browser chat - -By default, browser chat runs through WebUI's in-process legacy runtime. Advanced -self-hosted deployments can opt into routing new browser turns through a running -Hermes Gateway API server while preserving the existing WebUI `/api/chat/start` -and `/api/chat/stream` browser contract: - -```bash -HERMES_WEBUI_CHAT_BACKEND=gateway \ -HERMES_WEBUI_GATEWAY_BASE_URL=http://127.0.0.1:8642 \ -HERMES_WEBUI_GATEWAY_API_KEY=... \ -./ctl.sh restart -``` - -`HERMES_WEBUI_CHAT_BACKEND` is intentionally strict: only `gateway`, -`api_server`, or `api-server` enable the bridge. Generic truthy values such as -`1` or `true` are ignored so existing deployments do not change execution -ownership accidentally. If `HERMES_WEBUI_GATEWAY_API_KEY` is omitted, WebUI falls -back to `API_SERVER_KEY` when present. When Gateway returns HTTP 401, WebUI -reports a `gateway_auth_error` that points at this WebUI↔Gateway key mismatch -rather than showing the Gateway's generic provider-style "Invalid API key" body. -`/api/health/agent` also includes a redacted `gateway_chat` block so operators can -see whether gateway mode, base URL, and API-key presence are configured without -exposing the key value. That `gateway_chat` field is an operator diagnostic -payload only; it is not currently rendered as a user-facing health banner in the -browser UI. - -The bridge is best used by operators who already run Hermes Gateway/API Server -locally and want browser-originated chat to use the same runtime/tool path as -messaging surfaces. Attachments, cancellation, approvals, and clarify prompts -still follow WebUI's current compatibility path and may not match every messaging -surface until the runtime-adapter migration is complete. +Two optional, self-hosted-deployment features — attaching dynamic **session-recall prefill** to browser turns (Joplin/Obsidian/Notion/llm-wiki routers), and routing browser chat through a running **Hermes Gateway** — are documented in [`docs/advanced-chat-setup.md`](docs/advanced-chat-setup.md). Most users need neither. The bootstrap will: @@ -240,265 +164,6 @@ If an AI assistant is helping with install, reinstall, bootstrap, provider setup --- -## Docker - -**Pre-built images** (amd64 + arm64) are published to GHCR on every release. - -For a comprehensive setup guide covering all 3 compose files, common failure modes, and bind-mount migration, see [`docs/docker.md`](docs/docker.md). The README covers the 5-minute happy path. - -### 5-minute quickstart (single container) - -The simplest setup: one WebUI container that runs the agent in-process. - -```bash -git clone https://github.com/nesquena/hermes-webui -cd hermes-webui -cp .env.docker.example .env -# Edit .env if your host UID isn't 1000 (e.g. macOS where UIDs start at 501) -docker compose up -d -# Open http://localhost:8787 -``` - -Run Compose as the user who owns your Hermes home. `sudo docker compose up -d` can make `${HOME}` expand to the root user's home, so Docker mounts the wrong `.hermes` directory instead of your real `~/.hermes` and the WebUI starts with `config.yaml (not found, using defaults)`. Prefer adding your user to the Docker group and running `docker compose up -d`; if you must use sudo, set absolute paths first, for example `HERMES_HOME=/home/you/.hermes HERMES_WORKSPACE=/home/you/workspace sudo -E docker compose up -d`, then verify with `docker compose config`. - -The container auto-detects your UID/GID from the mounted `~/.hermes` volume so files written by the agent stay readable by you on the host. - -To enable password protection (required if you expose the port outside `127.0.0.1`): - -```bash -echo "HERMES_WEBUI_PASSWORD=change-me-to-something-strong" >> .env -docker compose up -d --force-recreate -``` - -### Manual `docker run` (no compose) - -```bash -docker pull ghcr.io/nesquena/hermes-webui:latest -docker run -d \ - -e WANTED_UID=$(id -u) -e WANTED_GID=$(id -g) \ - -v ~/.hermes:/home/hermeswebui/.hermes \ - -e HERMES_WEBUI_STATE_DIR=/home/hermeswebui/.hermes/webui \ - -v ~/workspace:/workspace \ - -p 127.0.0.1:8787:8787 \ - ghcr.io/nesquena/hermes-webui:latest -``` - -### Build locally - -```bash -docker build -t hermes-webui . -docker run -d \ - -e WANTED_UID=$(id -u) -e WANTED_GID=$(id -g) \ - -v ~/.hermes:/home/hermeswebui/.hermes \ - -e HERMES_WEBUI_STATE_DIR=/home/hermeswebui/.hermes/webui \ - -v ~/workspace:/workspace \ - -p 127.0.0.1:8787:8787 \ - hermes-webui -``` - -### Multi-container setups - -If you want the agent and WebUI in separate containers (for isolation, or because you're already running an agent gateway elsewhere): - -```bash -# Agent + WebUI -docker compose -f docker-compose.two-container.yml up -d - -# Agent + Dashboard + WebUI -docker compose -f docker-compose.three-container.yml up -d -``` - -Both compose files use **named Docker volumes** by default, which solves the UID/GID problem by construction. If you need bind mounts to share an existing host directory, see [`docs/docker.md`](docs/docker.md) for the full migration recipe. - -> **Known limitation (#681)**: in the two-container setup, tools triggered from the WebUI run in the **WebUI container**, not the agent container. If you need git/node/etc. on the WebUI's filesystem, either use the single-container setup, extend the WebUI Dockerfile, or use the community [all-in-one image](https://github.com/sunnysktsang/hermes-suite). -> -> **Source boundary note (#2453)**: the multi-container setup mounts `hermes-agent-src` read-only into the WebUI by default. This prevents WebUI-side source rewrites but is still an implementation-coupling bridge, not a stable Agent API boundary. See [`docs/rfcs/agent-source-boundary.md`](docs/rfcs/agent-source-boundary.md) for the current source/API decoupling inventory. - -### Common failure modes - -| Symptom | Likely cause | Fix | -|---|---|---| -| `PermissionError` at startup | UID mismatch on bind mount | Set `UID=$(id -u)` in `.env` | -| `.env: permission denied` (#1389) | `fix_credential_permissions()` enforced 0600 | Set `HERMES_SKIP_CHMOD=1` in `.env` | -| Workspace appears empty | UID mismatch on `/workspace` mount | Set `UID=$(id -u)` in `.env` | -| `git: command not found` in chat | Two-container architectural limit (#681) | Use single-container or extend Dockerfile | -| WebUI can't find agent source | `hermes-agent-src` volume misconfigured | Use the named volumes from compose files as-is | -| Podman shared `.hermes` fails | Podman 3.4 `keep-id` limitation | Use Podman 4+ or single-container | -| Host API at `localhost` fails from WebUI | Container `localhost` means the container, not your host (#3012) | Use `http://host.docker.internal:` on Docker Desktop, or `http://host.containers.internal:` on Podman | -| WebUI can't see `~/.hermes` after `sudo docker compose` | `${HOME}` expanded to the root user's home (#3006) | Run Compose as your user, or pass absolute `HERMES_HOME`/`HERMES_WORKSPACE` with `sudo -E` | - -For the deep dive on each of these, see [`docs/docker.md`](docs/docker.md). - -> **Note:** By default, Docker Compose binds to `127.0.0.1` (localhost only). -> To expose on a network, change the port to `"8787:8787"` in `docker-compose.yml` -> and set `HERMES_WEBUI_PASSWORD` to enable authentication. - ---- - -## What start.sh discovers automatically - -| Thing | How it finds it | -|---|---| -| Hermes agent dir | `HERMES_WEBUI_AGENT_DIR` env, then `$HERMES_HOME/hermes-agent` (Windows default `%LOCALAPPDATA%\hermes\hermes-agent`, POSIX default `~/.hermes/hermes-agent`), then sibling `../hermes-agent` | -| Python executable | Agent venv first, then `.venv` in this repo, then system `python3` | -| State directory | `HERMES_WEBUI_STATE_DIR` env, then `$HERMES_HOME/webui` (Windows default `%LOCALAPPDATA%\hermes\webui`, POSIX default `~/.hermes/webui`) | -| Default workspace | `HERMES_WEBUI_DEFAULT_WORKSPACE` env, then `~/workspace`, then state dir | -| Port | `HERMES_WEBUI_PORT` env or first argument, default `8787` | - -If discovery finds everything, nothing else is required. - ---- - -## Overrides (only needed if auto-detection misses) - -```bash -export HERMES_WEBUI_AGENT_DIR=/path/to/hermes-agent -export HERMES_WEBUI_PYTHON=/path/to/python -export HERMES_WEBUI_PORT=9000 -export HERMES_WEBUI_AUTO_INSTALL=1 # enable auto-install of agent deps (disabled by default) -./start.sh -``` - -Or inline: - -```bash -HERMES_WEBUI_AGENT_DIR=/custom/path ./start.sh 9000 -``` - -Full list of environment variables: - -| Variable | Default | Description | -|---|---|---| -| `HERMES_WEBUI_AGENT_DIR` | auto-discovered | Path to the hermes-agent checkout | -| `HERMES_WEBUI_PYTHON` | auto-discovered | Python executable | -| `HERMES_WEBUI_HOST` | `127.0.0.1` | Bind address (`0.0.0.0` for all IPv4, `::` for all IPv6, `::1` for IPv6 loopback) | -| `HERMES_WEBUI_PORT` | `8787` | Port | -| `HERMES_WEBUI_STATE_DIR` | `$HERMES_HOME/webui` (Windows default `%LOCALAPPDATA%\hermes\webui`, POSIX default `~/.hermes/webui`) | Where sessions and state are stored | -| `HERMES_WEBUI_DEFAULT_WORKSPACE` | `~/workspace` | Default workspace | -| `HERMES_WEBUI_DEFAULT_MODEL` | *(provider default)* | Optional model override; leave unset to use the active Hermes provider default | -| `HERMES_WEBUI_PASSWORD` | *(unset)* | Set to enable password authentication | -| `HERMES_WEBUI_CSP_CONNECT_EXTRA` | *(unset)* | Optional space-separated `http(s)://` or `ws(s)://` origins to append to the report-only CSP `connect-src` directive for reverse-proxy or tunnel deployments | -| `HERMES_WEBUI_EXTENSION_DIR` | *(unset)* | Optional local directory served at `/extensions/`; must point to an existing directory before extension injection is enabled | -| `HERMES_WEBUI_EXTENSION_SCRIPT_URLS` | *(unset)* | Optional comma-separated same-origin script URLs to inject; see [WebUI Extensions](docs/EXTENSIONS.md) | -| `HERMES_WEBUI_EXTENSION_STYLESHEET_URLS` | *(unset)* | Optional comma-separated same-origin stylesheet URLs to inject; see [WebUI Extensions](docs/EXTENSIONS.md) | -| `HERMES_HOME` | Windows: `%LOCALAPPDATA%\hermes`; POSIX: `~/.hermes` | Base directory for Hermes state (affects all paths) | -| `HERMES_CONFIG_PATH` | `$HERMES_HOME/config.yaml` | Path to Hermes config file | - ---- - -## Accessing from a remote machine - -The server binds to `127.0.0.1` by default (loopback only). If you are running -Hermes on a VPS or remote server, use an SSH tunnel from your local machine: - -```bash -ssh -N -L :127.0.0.1: @ -``` - -Example: - -```bash -ssh -N -L 8787:127.0.0.1:8787 user@your.server.com -``` - -Then open `http://localhost:8787` in your local browser. - -`start.sh` will print this command for you automatically when it detects you -are running over SSH. - ---- - -## Accessing on your phone with Tailscale - -[Tailscale](https://tailscale.com) is a zero-config mesh VPN built on -WireGuard. Install it on your server and your phone, and they join the same -private network -- no port forwarding, no SSH tunnels, no public exposure. - -The Hermes Web UI is fully responsive with a mobile-optimized layout -(hamburger sidebar, sidebar top tabs in the drawer, touch-friendly controls), -so it works well as a daily-driver agent interface from your phone. - -**Setup:** - -1. Install [Tailscale](https://tailscale.com/download) on your server and - your iPhone/Android. -2. Start the WebUI listening on all interfaces with password auth enabled: - -```bash -HERMES_WEBUI_HOST=0.0.0.0 HERMES_WEBUI_PASSWORD=your-secret ./start.sh -``` - -3. Open `http://:8787` in your phone's browser - (find your server's Tailscale IP in the Tailscale app or with - `tailscale ip -4` on the server). - -That's it. Traffic is encrypted end-to-end by WireGuard, and password auth -protects the UI at the application level. You can add it to your home screen -for an app-like experience. - -### Community field report: ARM64 Android via AVF - -A community report in [#2364](https://github.com/nesquena/hermes-webui/issues/2364) -documents Hermes Agent + WebUI running on a mid-range ARM64 Android phone inside -a Debian 12 VM via Android Virtualization Framework (AVF). The reported setup -used a Xiaomi Redmi Note 13 Pro 4G, 3.8 GiB RAM allocated to the VM, 8 visible -CPU cores, Chrome on Android at `localhost:8787`, and cloud-hosted inference. - -This is not an official support baseline or provider/model benchmark, but it is -a useful compatibility signal for mobile ARM64 experiments: the WebUI rendered -smoothly in Chrome, ARM64 Debian worked for the agent stack, and the total local -footprint was about 1.7 GB. Practical caveats from the report: first install can -take longer when dependencies compile from source, Android browser tabs may -reload when switching apps, and disabling battery optimization for the terminal -or VM host may be needed for longer-running sessions. - -> **Tip:** If using Docker, set `HERMES_WEBUI_HOST=0.0.0.0` in your -> `docker-compose.yml` environment (already the default) and set -> `HERMES_WEBUI_PASSWORD`. - ---- - -## Manual launch (without start.sh) - -If you prefer to launch the server directly: - -```bash -cd /path/to/hermes-agent # or wherever sys.path can find Hermes modules -HERMES_WEBUI_PORT=8787 venv/bin/python /path/to/hermes-webui/server.py -``` - -Note: use the agent venv Python (or any Python environment that has the Hermes agent dependencies installed). System Python will be missing `openai`, `httpx`, and other required packages. - -Health check: - -```bash -curl http://127.0.0.1:8787/health -``` - ---- - -## Running tests - -Tests discover the repo and the Hermes agent dynamically -- no hardcoded paths. - -```bash -cd hermes-webui -pytest tests/ -v --timeout=60 -``` - -Or using the agent venv explicitly: - -```bash -/path/to/hermes-agent/venv/bin/python -m pytest tests/ -v -``` - -Tests run against an isolated server with a separate state directory. -Production data and real cron jobs are never touched. Current snapshot: -**~7,150 tests collected** across **~700 test files**, run in CI on Python 3.11, -3.12, and 3.13 (3 parallel shards each). - ---- - ## Features ### Chat and agent @@ -620,6 +285,201 @@ Production data and real cron jobs are never touched. Current snapshot: --- +## Configuration & access + +`start.sh` auto-detects almost everything; the subsections below cover the knobs for when it can't, and how to reach the UI remotely. + +### What start.sh discovers automatically + +| Thing | How it finds it | +|---|---| +| Hermes agent dir | `HERMES_WEBUI_AGENT_DIR` env, then `$HERMES_HOME/hermes-agent` (Windows default `%LOCALAPPDATA%\hermes\hermes-agent`, POSIX default `~/.hermes/hermes-agent`), then sibling `../hermes-agent` | +| Python executable | Agent venv first, then `.venv` in this repo, then system `python3` | +| State directory | `HERMES_WEBUI_STATE_DIR` env, then `$HERMES_HOME/webui` (Windows default `%LOCALAPPDATA%\hermes\webui`, POSIX default `~/.hermes/webui`) | +| Default workspace | `HERMES_WEBUI_DEFAULT_WORKSPACE` env, then `~/workspace`, then state dir | +| Port | `HERMES_WEBUI_PORT` env or first argument, default `8787` | + +If discovery finds everything, nothing else is required. + +--- + +### Overrides (only needed if auto-detection misses) + +```bash +export HERMES_WEBUI_AGENT_DIR=/path/to/hermes-agent +export HERMES_WEBUI_PYTHON=/path/to/python +export HERMES_WEBUI_PORT=9000 +export HERMES_WEBUI_AUTO_INSTALL=1 # enable auto-install of agent deps (disabled by default) +./start.sh +``` + +Or inline: + +```bash +HERMES_WEBUI_AGENT_DIR=/custom/path ./start.sh 9000 +``` + +Full list of environment variables: + +| Variable | Default | Description | +|---|---|---| +| `HERMES_WEBUI_AGENT_DIR` | auto-discovered | Path to the hermes-agent checkout | +| `HERMES_WEBUI_PYTHON` | auto-discovered | Python executable | +| `HERMES_WEBUI_HOST` | `127.0.0.1` | Bind address (`0.0.0.0` for all IPv4, `::` for all IPv6, `::1` for IPv6 loopback) | +| `HERMES_WEBUI_PORT` | `8787` | Port | +| `HERMES_WEBUI_STATE_DIR` | `$HERMES_HOME/webui` (Windows default `%LOCALAPPDATA%\hermes\webui`, POSIX default `~/.hermes/webui`) | Where sessions and state are stored | +| `HERMES_WEBUI_DEFAULT_WORKSPACE` | `~/workspace` | Default workspace | +| `HERMES_WEBUI_DEFAULT_MODEL` | *(provider default)* | Optional model override; leave unset to use the active Hermes provider default | +| `HERMES_WEBUI_PASSWORD` | *(unset)* | Set to enable password authentication | +| `HERMES_WEBUI_CSP_CONNECT_EXTRA` | *(unset)* | Optional space-separated `http(s)://` or `ws(s)://` origins to append to the report-only CSP `connect-src` directive for reverse-proxy or tunnel deployments | +| `HERMES_WEBUI_EXTENSION_DIR` | *(unset)* | Optional local directory served at `/extensions/`; must point to an existing directory before extension injection is enabled | +| `HERMES_WEBUI_EXTENSION_SCRIPT_URLS` | *(unset)* | Optional comma-separated same-origin script URLs to inject; see [WebUI Extensions](docs/EXTENSIONS.md) | +| `HERMES_WEBUI_EXTENSION_STYLESHEET_URLS` | *(unset)* | Optional comma-separated same-origin stylesheet URLs to inject; see [WebUI Extensions](docs/EXTENSIONS.md) | +| `HERMES_HOME` | Windows: `%LOCALAPPDATA%\hermes`; POSIX: `~/.hermes` | Base directory for Hermes state (affects all paths) | +| `HERMES_CONFIG_PATH` | `$HERMES_HOME/config.yaml` | Path to Hermes config file | + +--- + +### Remote access (SSH tunnel, Tailscale, phone) + +The server binds to `127.0.0.1` by default. To reach it from another machine use an SSH tunnel (`ssh -N -L 8787:127.0.0.1:8787 user@host`, which `start.sh` prints for you over SSH), or join your server and phone to a [Tailscale](https://tailscale.com) network and browse to `http://:8787` with `HERMES_WEBUI_HOST=0.0.0.0` + `HERMES_WEBUI_PASSWORD` set. Full walkthrough (incl. a community ARM64-Android field report): [`docs/remote-access.md`](docs/remote-access.md). + +### Manual launch (without start.sh) + +If you prefer to launch the server directly: + +```bash +cd /path/to/hermes-agent # or wherever sys.path can find Hermes modules +HERMES_WEBUI_PORT=8787 venv/bin/python /path/to/hermes-webui/server.py +``` + +Note: use the agent venv Python (or any Python environment that has the Hermes agent dependencies installed). System Python will be missing `openai`, `httpx`, and other required packages. + +Health check: + +```bash +curl http://127.0.0.1:8787/health +``` + +--- + +## Docker + +**Pre-built images** (amd64 + arm64) are published to GHCR on every release. + +For a comprehensive setup guide covering all 3 compose files, common failure modes, and bind-mount migration, see [`docs/docker.md`](docs/docker.md). The README covers the 5-minute happy path. + +### 5-minute quickstart (single container) + +The simplest setup: one WebUI container that runs the agent in-process. + +```bash +git clone https://github.com/nesquena/hermes-webui +cd hermes-webui +cp .env.docker.example .env +# Edit .env if your host UID isn't 1000 (e.g. macOS where UIDs start at 501) +docker compose up -d +# Open http://localhost:8787 +``` + +Run Compose as the user who owns your Hermes home. `sudo docker compose up -d` can make `${HOME}` expand to the root user's home, so Docker mounts the wrong `.hermes` directory instead of your real `~/.hermes` and the WebUI starts with `config.yaml (not found, using defaults)`. Prefer adding your user to the Docker group and running `docker compose up -d`; if you must use sudo, set absolute paths first, for example `HERMES_HOME=/home/you/.hermes HERMES_WORKSPACE=/home/you/workspace sudo -E docker compose up -d`, then verify with `docker compose config`. + +The container auto-detects your UID/GID from the mounted `~/.hermes` volume so files written by the agent stay readable by you on the host. + +To enable password protection (required if you expose the port outside `127.0.0.1`): + +```bash +echo "HERMES_WEBUI_PASSWORD=change-me-to-something-strong" >> .env +docker compose up -d --force-recreate +``` + +### Manual `docker run` (no compose) + +```bash +docker pull ghcr.io/nesquena/hermes-webui:latest +docker run -d \ + -e WANTED_UID=$(id -u) -e WANTED_GID=$(id -g) \ + -v ~/.hermes:/home/hermeswebui/.hermes \ + -e HERMES_WEBUI_STATE_DIR=/home/hermeswebui/.hermes/webui \ + -v ~/workspace:/workspace \ + -p 127.0.0.1:8787:8787 \ + ghcr.io/nesquena/hermes-webui:latest +``` + +### Build locally + +```bash +docker build -t hermes-webui . +docker run -d \ + -e WANTED_UID=$(id -u) -e WANTED_GID=$(id -g) \ + -v ~/.hermes:/home/hermeswebui/.hermes \ + -e HERMES_WEBUI_STATE_DIR=/home/hermeswebui/.hermes/webui \ + -v ~/workspace:/workspace \ + -p 127.0.0.1:8787:8787 \ + hermes-webui +``` + +### Multi-container setups + +If you want the agent and WebUI in separate containers (for isolation, or because you're already running an agent gateway elsewhere): + +```bash +# Agent + WebUI +docker compose -f docker-compose.two-container.yml up -d + +# Agent + Dashboard + WebUI +docker compose -f docker-compose.three-container.yml up -d +``` + +Both compose files use **named Docker volumes** by default, which solves the UID/GID problem by construction. If you need bind mounts to share an existing host directory, see [`docs/docker.md`](docs/docker.md) for the full migration recipe. + +> **Known limitation (#681)**: in the two-container setup, tools triggered from the WebUI run in the **WebUI container**, not the agent container. If you need git/node/etc. on the WebUI's filesystem, either use the single-container setup, extend the WebUI Dockerfile, or use the community [all-in-one image](https://github.com/sunnysktsang/hermes-suite). +> +> **Source boundary note (#2453)**: the multi-container setup mounts `hermes-agent-src` read-only into the WebUI by default. This prevents WebUI-side source rewrites but is still an implementation-coupling bridge, not a stable Agent API boundary. See [`docs/rfcs/agent-source-boundary.md`](docs/rfcs/agent-source-boundary.md) for the current source/API decoupling inventory. + +### Common failure modes + +| Symptom | Likely cause | Fix | +|---|---|---| +| `PermissionError` at startup | UID mismatch on bind mount | Set `UID=$(id -u)` in `.env` | +| `.env: permission denied` (#1389) | `fix_credential_permissions()` enforced 0600 | Set `HERMES_SKIP_CHMOD=1` in `.env` | +| Workspace appears empty | UID mismatch on `/workspace` mount | Set `UID=$(id -u)` in `.env` | +| `git: command not found` in chat | Two-container architectural limit (#681) | Use single-container or extend Dockerfile | +| WebUI can't find agent source | `hermes-agent-src` volume misconfigured | Use the named volumes from compose files as-is | +| Podman shared `.hermes` fails | Podman 3.4 `keep-id` limitation | Use Podman 4+ or single-container | +| Host API at `localhost` fails from WebUI | Container `localhost` means the container, not your host (#3012) | Use `http://host.docker.internal:` on Docker Desktop, or `http://host.containers.internal:` on Podman | +| WebUI can't see `~/.hermes` after `sudo docker compose` | `${HOME}` expanded to the root user's home (#3006) | Run Compose as your user, or pass absolute `HERMES_HOME`/`HERMES_WORKSPACE` with `sudo -E` | + +For the deep dive on each of these, see [`docs/docker.md`](docs/docker.md). + +> **Note:** By default, Docker Compose binds to `127.0.0.1` (localhost only). +> To expose on a network, change the port to `"8787:8787"` in `docker-compose.yml` +> and set `HERMES_WEBUI_PASSWORD` to enable authentication. + +--- + +## Running tests + +Tests discover the repo and the Hermes agent dynamically -- no hardcoded paths. + +```bash +cd hermes-webui +pytest tests/ -v --timeout=60 +``` + +Or using the agent venv explicitly: + +```bash +/path/to/hermes-agent/venv/bin/python -m pytest tests/ -v +``` + +Tests run against an isolated server with a separate state directory. +Production data and real cron jobs are never touched. Current snapshot: +**~7,150 tests collected** across **~700 test files**, run in CI on Python 3.11, +3.12, and 3.13 (3 parallel shards each). + +--- + ## Architecture No build step, no framework, no bundler — a Python standard-library HTTP server @@ -688,6 +548,8 @@ Full design notes and the endpoint catalog are in [`ARCHITECTURE.md`](ARCHITECTU - [`docs/EXTENSIONS.md`](docs/EXTENSIONS.md) — administrator-controlled WebUI extension injection **Deploying & operating** +- [`docs/remote-access.md`](docs/remote-access.md) — SSH tunnel, Tailscale, and phone access (incl. a community ARM64-Android field report) +- [`docs/advanced-chat-setup.md`](docs/advanced-chat-setup.md) — optional dynamic recall-prefill and Gateway-backed browser chat for self-hosted deployments - [`docs/docker.md`](docs/docker.md) — Docker compose setup, common failures, and bind-mount migration - [`docs/supervisor.md`](docs/supervisor.md) — launchd, systemd, supervisord, runit, and s6 process-supervisor setup - [`docs/wsl-autostart.md`](docs/wsl-autostart.md) — WSL2 auto-start at Windows login @@ -708,6 +570,8 @@ Full design notes and the endpoint catalog are in [`ARCHITECTURE.md`](ARCHITECTU - [`SPRINTS.md`](SPRINTS.md) — forward sprint plan with CLI + Claude parity targets - [`CONTRIBUTORS.md`](CONTRIBUTORS.md) — the full community credit roll +--- + ## Contributors Hermes WebUI is built with help from the open-source community. Every PR — whether merged directly, absorbed into a batch release, or salvaged from a larger proposal — shapes the project, and we're grateful to everyone who has taken the time to contribute. @@ -832,6 +696,8 @@ A comprehensive CSRF / SSRF / XSS / env-race-condition audit that shipped in v0. **[@TaraTheStar](https://github.com/TaraTheStar)** — Bot name + thinking blocks + login refactor (PRs #132, #176, #181) Configurable assistant display name, thinking/reasoning block display, and a login page refactor. +--- + ## Repo ``` diff --git a/docs/advanced-chat-setup.md b/docs/advanced-chat-setup.md new file mode 100644 index 000000000..f0c65077d --- /dev/null +++ b/docs/advanced-chat-setup.md @@ -0,0 +1,83 @@ +# Advanced chat setup + +Two optional features for self-hosted Hermes WebUI deployments. **Most users need neither** — the defaults (in-process chat, no prefill) work out of the box. + +## Session recall prefill + +WebUI can attach ephemeral prefill messages to new browser-originated +agent turns. This is useful when a deployment already has a local recall or +router script for Joplin, Obsidian, Notion, llm-wiki, or another third-party +notes source and wants browser chat to know where durable context lives. + +Prefer a compact router-style prefill (for example, "Joplin has the durable +project context; use the available notes/search tools before answering +detail-dependent questions") instead of dumping the full note corpus into every +new browser session. The prefill should point the agent toward retrieval; the +notes/search tools should provide the specific facts on demand. + +Static JSON remains supported through `prefill_messages_file` or +`HERMES_PREFILL_MESSAGES_FILE`. For dynamic recall, opt in explicitly with a +WebUI-specific script hook: + +```yaml +webui_prefill_messages_script: + - python3 + - /path/to/notes_recall.py +webui_prefill_messages_script_timeout: 5 +``` + +or: + +```bash +HERMES_WEBUI_PREFILL_MESSAGES_SCRIPT="python3 /path/to/notes_recall.py" \ +HERMES_WEBUI_PREFILL_MESSAGES_SCRIPT_TIMEOUT=5 \ +./ctl.sh restart +``` + +The script may print either an OpenAI-style JSON message list, a JSON object with +a `messages` list, or plain text; plain text is wrapped as one `user` prefill +message so dynamic recall text becomes ordinary context instead of an extra +system instruction. If the hook must provide system-level guidance, emit JSON +messages with an explicit `role: "system"` entry instead. Script output is capped +at 256 KiB before parsing. Parsed prefill context is then bounded by +`webui_prefill_context_max_chars` or `HERMES_WEBUI_PREFILL_CONTEXT_MAX_CHARS` +(default: 12,000 characters; set to `0` to disable). When a dynamic script +exceeds the budget and a compact static prefill file is configured, WebUI falls +back to that file. If no compact fallback is available, WebUI injects a short +retrieval instruction instead of sending the oversized note/body payload with +every new browser turn. The browser only receives a compact status event +(`source`, `label`, message count, compaction metadata, and redacted errors), +never the prefill message bodies. + +## Gateway-backed browser chat + +By default, browser chat runs through WebUI's in-process legacy runtime. Advanced +self-hosted deployments can opt into routing new browser turns through a running +Hermes Gateway API server while preserving the existing WebUI `/api/chat/start` +and `/api/chat/stream` browser contract: + +```bash +HERMES_WEBUI_CHAT_BACKEND=gateway \ +HERMES_WEBUI_GATEWAY_BASE_URL=http://127.0.0.1:8642 \ +HERMES_WEBUI_GATEWAY_API_KEY=... \ +./ctl.sh restart +``` + +`HERMES_WEBUI_CHAT_BACKEND` is intentionally strict: only `gateway`, +`api_server`, or `api-server` enable the bridge. Generic truthy values such as +`1` or `true` are ignored so existing deployments do not change execution +ownership accidentally. If `HERMES_WEBUI_GATEWAY_API_KEY` is omitted, WebUI falls +back to `API_SERVER_KEY` when present. When Gateway returns HTTP 401, WebUI +reports a `gateway_auth_error` that points at this WebUI↔Gateway key mismatch +rather than showing the Gateway's generic provider-style "Invalid API key" body. +`/api/health/agent` also includes a redacted `gateway_chat` block so operators can +see whether gateway mode, base URL, and API-key presence are configured without +exposing the key value. That `gateway_chat` field is an operator diagnostic +payload only; it is not currently rendered as a user-facing health banner in the +browser UI. + +The bridge is best used by operators who already run Hermes Gateway/API Server +locally and want browser-originated chat to use the same runtime/tool path as +messaging surfaces. Attachments, cancellation, approvals, and clarify prompts +still follow WebUI's current compatibility path and may not match every messaging +surface until the runtime-adapter migration is complete. diff --git a/docs/remote-access.md b/docs/remote-access.md new file mode 100644 index 000000000..a52994973 --- /dev/null +++ b/docs/remote-access.md @@ -0,0 +1,75 @@ +# Remote access + +How to reach a self-hosted Hermes WebUI from another machine or your phone. + +## Accessing from a remote machine + +The server binds to `127.0.0.1` by default (loopback only). If you are running +Hermes on a VPS or remote server, use an SSH tunnel from your local machine: + +```bash +ssh -N -L :127.0.0.1: @ +``` + +Example: + +```bash +ssh -N -L 8787:127.0.0.1:8787 user@your.server.com +``` + +Then open `http://localhost:8787` in your local browser. + +`start.sh` will print this command for you automatically when it detects you +are running over SSH. + +--- + +## Accessing on your phone with Tailscale + +[Tailscale](https://tailscale.com) is a zero-config mesh VPN built on +WireGuard. Install it on your server and your phone, and they join the same +private network -- no port forwarding, no SSH tunnels, no public exposure. + +The Hermes Web UI is fully responsive with a mobile-optimized layout +(hamburger sidebar, sidebar top tabs in the drawer, touch-friendly controls), +so it works well as a daily-driver agent interface from your phone. + +**Setup:** + +1. Install [Tailscale](https://tailscale.com/download) on your server and + your iPhone/Android. +2. Start the WebUI listening on all interfaces with password auth enabled: + +```bash +HERMES_WEBUI_HOST=0.0.0.0 HERMES_WEBUI_PASSWORD=your-secret ./start.sh +``` + +3. Open `http://:8787` in your phone's browser + (find your server's Tailscale IP in the Tailscale app or with + `tailscale ip -4` on the server). + +That's it. Traffic is encrypted end-to-end by WireGuard, and password auth +protects the UI at the application level. You can add it to your home screen +for an app-like experience. + +### Community field report: ARM64 Android via AVF + +A community report in [#2364](https://github.com/nesquena/hermes-webui/issues/2364) +documents Hermes Agent + WebUI running on a mid-range ARM64 Android phone inside +a Debian 12 VM via Android Virtualization Framework (AVF). The reported setup +used a Xiaomi Redmi Note 13 Pro 4G, 3.8 GiB RAM allocated to the VM, 8 visible +CPU cores, Chrome on Android at `localhost:8787`, and cloud-hosted inference. + +This is not an official support baseline or provider/model benchmark, but it is +a useful compatibility signal for mobile ARM64 experiments: the WebUI rendered +smoothly in Chrome, ARM64 Debian worked for the agent stack, and the total local +footprint was about 1.7 GB. Practical caveats from the report: first install can +take longer when dependencies compile from source, Android browser tabs may +reload when switching apps, and disabling battery optimization for the terminal +or VM host may be needed for longer-running sessions. + +> **Tip:** If using Docker, set `HERMES_WEBUI_HOST=0.0.0.0` in your +> `docker-compose.yml` environment (already the default) and set +> `HERMES_WEBUI_PASSWORD`. + +--- From 099dcf52feb38c78a52b37fa93904791836228de Mon Sep 17 00:00:00 2001 From: nesquena-hermes <[email protected]> Date: Mon, 1 Jun 2026 02:36:25 +0000 Subject: [PATCH 5/5] test: point gateway_chat doc-contract test at docs/advanced-chat-setup.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The README IA pass moved the Gateway-backed-chat operator docs out of the README into docs/advanced-chat-setup.md (niche self-hosted feature). The contract test test_gateway_chat_health_payload_is_documented_as_operator_diagnostic_only asserted those phrases live in README.md — update it to follow the content to its new home (CHANGELOG entry unchanged). Contract test moves with the content per docs/CONTRACTS.md. Full suite: 7138 passed, 0 failed. --- tests/test_webui_gateway_chat_backend.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/test_webui_gateway_chat_backend.py b/tests/test_webui_gateway_chat_backend.py index e154df682..9f3291f43 100644 --- a/tests/test_webui_gateway_chat_backend.py +++ b/tests/test_webui_gateway_chat_backend.py @@ -201,9 +201,14 @@ def test_gateway_auth_label_i18n_key_exists_for_every_locale(): def test_gateway_chat_health_payload_is_documented_as_operator_diagnostic_only(): - readme = Path("README.md").read_text(encoding="utf-8") + # The Gateway-backed-chat operator docs moved out of the README into + # docs/advanced-chat-setup.md during the v0.51.192 README IA pass (it's a + # niche self-hosted feature). The contract — that gateway_chat is documented + # as an operator-only diagnostic, not a user-facing banner — now lives there. + # CHANGELOG keeps its release-note entry. (Contract test moved with content.) + advanced = Path("docs/advanced-chat-setup.md").read_text(encoding="utf-8") changelog = Path("CHANGELOG.md").read_text(encoding="utf-8") - for text in (readme, changelog): + for text in (advanced, changelog): assert "gateway_chat" in text assert "operator diagnostic" in text assert "not currently rendered as a user-facing health banner" in text