diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index 474d251..8dabfd0 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -31,25 +31,25 @@ This makes the code easy to modify from a terminal or by an agent.
start.sh Discovery script: finds agent dir, Python, starts server.
api/
__init__.py Package marker
- routes.py All GET + POST route handlers (~802 lines)
- config.py Shared configuration, constants, global state, model discovery (~453 lines)
+ routes.py All GET + POST route handlers (~1016 lines)
+ config.py Shared configuration, constants, global state, model discovery (~640 lines)
helpers.py HTTP helpers: j(), bad(), require(), safe_resolve() (~57 lines)
models.py Session model + CRUD (~114 lines)
workspace.py File ops: list_dir, read_file_content, workspace helpers (~77 lines)
upload.py Multipart parser, file upload handler (~77 lines)
- streaming.py SSE engine, run_agent integration, cancel support (~218 lines)
+ streaming.py SSE engine, run_agent integration, cancel support (~222 lines)
static/
index.html HTML template (served from disk)
style.css All CSS
- ui.js DOM helpers, renderMd, tool cards, model dropdown (~671 lines)
- workspace.js File tree, preview, file ops (~168 lines)
- sessions.js Session CRUD, list rendering, search (~206 lines)
- messages.js send(), SSE event handlers, approval, transcript (~310 lines)
- panels.js Cron, skills, memory, workspace, todo, switchPanel (~600 lines)
- boot.js Event wiring + boot IIFE (~154 lines)
+ ui.js DOM helpers, renderMd, tool cards, model dropdown (~809 lines)
+ workspace.js File tree, preview, file ops (~169 lines)
+ sessions.js Session CRUD, list rendering, search, SVG icons, overlay actions (~532 lines)
+ messages.js send(), SSE event handlers, approval, transcript (~293 lines)
+ panels.js Cron, skills, memory, workspace, todo, switchPanel (~771 lines)
+ boot.js Event wiring + boot IIFE (~175 lines)
tests/
conftest.py Isolated test server (port 8788, separate HERMES_HOME) (~240 lines)
- test_sprint1-11.py Feature tests per sprint (13 files)
+ test_sprint1-11.py Feature tests per sprint (13 files, Sprints 1-11)
test_regressions.py Permanent regression gate
AGENTS.md Instruction file for agents working in this directory.
ROADMAP.md Feature and product roadmap document.
@@ -151,10 +151,14 @@ Session is a plain Python class (not a dataclass, not SQLAlchemy):
session_id hex string, 12 chars (uuid4().hex[:12])
title string, auto-set from first user message
workspace absolute path string, resolved at creation
- model OpenRouter model ID string (e.g. "anthropic/claude-sonnet-4.6")
+ model model ID string (e.g. "anthropic/claude-sonnet-4.6")
messages list of OpenAI-format message dicts
created_at float Unix timestamp
updated_at float Unix timestamp, updated on every save()
+ pinned bool, default False (Sprint 12)
+ archived bool, default False (Sprint 14)
+ project_id string or null, FK to projects.json (Sprint 15)
+ tool_calls list of tool call dicts (Sprint 10)
Key methods:
path (property) Returns SESSION_DIR/{session_id}.json
@@ -326,16 +330,20 @@ read_file_content(workspace, rel):
### 5.1 Structure
The frontend is served from static/ as separate files: one HTML template, one CSS file,
-and six JavaScript modules (~2,025 lines total). External dependency: Prism.js from CDN
-(syntax highlighting, loaded async/deferred).
+and six JavaScript modules (~2,750 lines total). External dependencies: Prism.js (syntax
+highlighting) and Mermaid.js (diagrams) from CDN, both loaded async/deferred with SRI hashes.
Six JS modules loaded in order at end of
:
- 1. ui.js (~589 lines) DOM helpers, renderMd, tool card rendering, global state
- 2. workspace.js (~168 lines) File tree, preview, file operations
- 3. sessions.js (~206 lines) Session CRUD, list rendering, search
- 4. messages.js (~310 lines) send(), SSE event handlers, approval, transcript
- 5. panels.js (~600 lines) Cron, skills, memory, workspace, todo, switchPanel
- 6. boot.js (~152 lines) Event wiring + boot IIFE
+ 1. ui.js (~809 lines) DOM helpers, renderMd, tool card rendering, global state
+ 2. workspace.js (~169 lines) File tree, preview, file operations
+ 3. sessions.js (~532 lines) Session CRUD, list rendering, search, SVG icons, overlay actions, project picker
+ 4. messages.js (~293 lines) send(), SSE event handlers, approval, transcript
+ 5. panels.js (~771 lines) Cron, skills, memory, workspace, todo, switchPanel
+ 6. boot.js (~175 lines) Event wiring + boot IIFE
+
+sessions.js defines an `ICONS` constant at module level with hardcoded SVG strings for all
+session action buttons (pin, unpin, folder, archive, unarchive, duplicate, trash). All icons
+inherit `currentColor` for consistent theming.
Three-panel layout (in static/index.html):
@@ -604,26 +612,27 @@ Split server.py into a proper package. Completed across Sprints 4-10.
Current structure:
/
- server.py Entry point + HTTP Handler routing (~704 lines)
+ server.py Entry point + HTTP Handler dispatch (~76 lines)
api/
__init__.py
- config.py Configuration, constants, global state (~273 lines)
+ routes.py All GET + POST route handlers (~1016 lines)
+ config.py Configuration, constants, global state, model discovery (~640 lines)
helpers.py HTTP helpers: j(), bad(), require(), safe_resolve() (~57 lines)
- models.py Session model + CRUD (~114 lines)
+ models.py Session model + CRUD (~132 lines)
workspace.py File ops, workspace management (~77 lines)
upload.py Multipart parser, file upload handler (~77 lines)
- streaming.py SSE engine, run_agent, cancel support (~218 lines)
+ streaming.py SSE engine, run_agent, cancel support (~222 lines)
static/
index.html HTML document (served from disk)
- style.css All CSS
+ style.css All CSS (~560 lines)
ui.js, workspace.js, sessions.js, messages.js, panels.js, boot.js
tests/
conftest.py Isolated test server on port 8788
- test_sprint1-10.py Feature tests per sprint (12 files)
+ test_sprint1-11.py Feature tests per sprint (13 files)
test_regressions.py Permanent regression gate
-Remaining: server.py still has all 49 route handlers in one do_GET/do_POST class.
-Sprint 11 plans extracting these to api/routes.py, making server.py a ~50-line shell.
+Route extraction to api/routes.py completed in Sprint 11. server.py is now a ~76-line
+thin shell: Handler class with structured logging, dispatch to routes, and main().
### Phase B: Thread-Safe Request Context (Priority: Critical, Effort: Medium)
@@ -718,10 +727,10 @@ Optional password gate for non-SSH-tunnel deployments.
### Phase I: Test Infrastructure -- COMPLETE
-190 tests across 12 test files + regression gate. Isolated test server on port 8788
+237 tests across 13 test files + regression gate. Isolated test server on port 8788
with separate HERMES_HOME, wiped per run. Production data never touched.
-Test files: `test_sprint1.py` through `test_sprint10.py`, `test_regressions.py`.
+Test files: `test_sprint1.py` through `test_sprint11.py`, `test_regressions.py`.
Fixtures in `conftest.py`: auto-cleanup, cron isolation, workspace reset.
Remaining: no CI (GitHub Actions), no frontend tests (browser-based).
diff --git a/BUGS.md b/BUGS.md
index 62be3ce..b0ed582 100644
--- a/BUGS.md
+++ b/BUGS.md
@@ -1,18 +1,40 @@
# Bugs Backlog
-This file tracks UI bugs and polish items to address in a future sprint.
+This file tracks UI bugs and polish items. Fixed items are kept for reference.
-## ~~Conversation list title truncation / hover actions~~ — Fixed (Sprint 16)
+---
+
+## Open Bugs
+
+*No open bugs at this time.*
+
+---
+
+## Fixed
+
+### ~~Session title truncation / hover actions~~ -- Fixed (Sprint 16)
- **Was:** Action icons reserved ~30px of space even when invisible, truncating titles.
- **Fix:** Wrapped all action buttons in a `.session-actions` overlay container with `position:absolute`. Titles now use full available width. Actions appear on hover with a gradient fade from the right edge.
-## ~~Folder/project assignment interaction feels sticky~~ — Fixed (Sprint 16)
+### ~~Folder/project assignment interaction feels sticky~~ -- Fixed (Sprint 16)
- **Was:** Folder icon stayed permanently visible (blue, 60% opacity) when a session belonged to a project.
- **Fix:** Replaced `.has-project` persistent button with a colored left border matching the project color. The folder button now only appears in the hover overlay like all other actions.
+### ~~Project picker clipping and width~~ -- Fixed (v0.17.3)
+
+- **Was:** Picker was clipped by `overflow:hidden` on `.session-item` ancestors. With `position:fixed`, no containing block constrained width -- picker stretched to full viewport.
+- **Fix:** Dynamic width calculation (min 160px, max 220px). Event listener reordering. Cleanup sequence corrected. (PR #25)
+
+### ~~NameError crash in model discovery~~ -- Fixed (v0.17.3)
+
+- **Was:** `logger.debug()` called in custom endpoint `except` block, but `logger` was never imported in `config.py`. Every failed endpoint fetch crashed with `NameError`.
+- **Fix:** Replaced with silent `pass` -- unreachable endpoints are expected when no local LLM is configured. (PR #24)
+
+---
+
## Notes
-- Both issues resolved in Sprint 16 (Session Sidebar Visual Polish).
-- Icons replaced from inconsistent emoji HTML entities to monochrome SVG line icons.
+- Sprint 16 replaced all emoji HTML entities with monochrome SVG line icons (`ICONS` constant in `sessions.js`).
+- All session action buttons now use the overlay pattern for consistent UX.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1608cf2..af17689 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,52 @@
---
+## [v0.18] Sprint 16 -- Session Sidebar Visual Polish
+*April 2, 2026 | 237 tests*
+
+### Features
+- **SVG action icons.** Replaced all emoji HTML entities (star, folder, box,
+ duplicate, trash) with monochrome SVG line icons that inherit `currentColor`.
+ Consistent rendering across macOS, Linux, and Windows. Defined in a top-level
+ `ICONS` constant in `sessions.js`.
+- **Action buttons overlay.** All session action buttons (pin, move, archive,
+ duplicate, trash) wrapped in a `.session-actions` container with
+ `position:absolute`. Titles now use full available width instead of being
+ truncated by invisible buttons. Actions appear on hover with a gradient fade
+ from the right edge. Overlay auto-hides during inline rename via
+ `:has(.session-title-input)`.
+- **Pin indicator.** Small gold filled-star icon rendered inline before the
+ title only when pinned. Unpinned sessions get full title width with zero
+ space reservation.
+- **Project border indicator.** Sessions assigned to a project show a colored
+ left border matching the project color, replacing the old always-visible
+ blue folder button.
+
+### Bug Fixes
+- **Session title truncation.** Action icons reserved ~30px of space even when
+ invisible, truncating titles. Fixed by overlay container approach.
+- **Folder button felt sticky.** Replaced `.has-project` persistent blue button
+ with colored left border. Folder button now only appears in hover overlay.
+
+---
+
+## [v0.17.3] Bug Fixes
+*April 2, 2026*
+
+### Bug Fixes
+- **NameError crash in model discovery.** `logger.debug()` was called in the
+ custom endpoint `except` block in `config.py`, but `logger` was never
+ imported. Every failed custom endpoint fetch crashed with `NameError`,
+ returning HTTP 500 for `/api/models`. Replaced with silent `pass` since
+ unreachable endpoints are expected. (PR #24)
+- **Project picker clipping and width.** Picker was clipped by
+ `overflow:hidden` on ancestor elements. Width calculation improved with
+ dynamic sizing (min 160px, max 220px). Event listener `close` handler
+ moved after DOM append to fix reference-before-definition. Reordered
+ `picker.remove()` before `removeEventListener` for correct cleanup. (PR #25)
+
+---
+
## [v0.17.2] Model Update
*April 2, 2026*
@@ -509,4 +555,4 @@ Three-panel layout: sessions sidebar, chat area, workspace panel.
---
-*Last updated: v0.16.2, April 1, 2026 | Tests: 247*
+*Last updated: v0.18, April 2, 2026 | Tests: 237*
diff --git a/ROADMAP.md b/ROADMAP.md
index 7784eb7..eb98218 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -3,7 +3,7 @@
> Goal: Full 1:1 parity with the Hermes CLI experience via a clean dark web UI.
> Everything you can do from the CLI terminal, you can do from this UI.
>
-> Last updated: Sprint 15 / v0.17.1 (April 2, 2026)
+> Last updated: Sprint 16 / v0.18 (April 2, 2026)
> Tests: 237 passing
> Source: /
@@ -32,6 +32,7 @@
| Sprint 13 | Alerts + polish | Cron completion alerts (polling + badge), background error banner, session duplicate, browser tab title | 221 |
| Sprint 14 | Visual polish + workspace ops | Mermaid diagrams, message timestamps, file rename, folder create, session tags, session archive | 233 |
| Sprint 15 | Session projects + code copy | Session projects/folders, code block copy button, tool card expand/collapse toggle | 237 |
+| Sprint 16 | Session sidebar visual polish | SVG action icons, overlay hover actions, pin indicator, project border, custom model discovery, GLM-5.1 | 237 |
---
@@ -39,10 +40,10 @@
| Layer | Location | Status |
|-------|----------|--------|
-| Python server | /server.py (~76 lines) + api/ modules (~1900 lines) | Thin shell + business logic in api/ |
+| Python server | /server.py (~76 lines) + api/ modules (~2145 lines) | Thin shell + business logic in api/ |
| HTML template | /static/index.html | Served from disk |
-| CSS | /static/style.css | Served from disk |
-| JavaScript | /static/{ui,workspace,sessions,messages,panels,boot}.js | 6 modules, ~2250 lines total |
+| CSS | /static/style.css (~560 lines) | Served from disk |
+| JavaScript | /static/{ui,workspace,sessions,messages,panels,boot}.js | 6 modules, ~2750 lines total |
| Runtime state | ~/.hermes/webui-mvp/sessions/ | Session JSON files |
| Test server | Port 8788, state dir ~/.hermes/webui-mvp-test/ | Isolated, wiped per run |
| Production server | Port 8787 | SSH tunnel from Mac |
@@ -55,6 +56,7 @@
- [x] Send messages, get SSE-streaming responses
- [x] Switch models per session (10 models, grouped by provider)
- [x] Multi-provider API support: use any Hermes agent API provider (OpenAI, Anthropic, Google, etc.) directly, not just OpenRouter (Sprint 11)
+- [x] Custom endpoint model discovery: auto-detect models from Ollama, LM Studio, and other local LLM servers via base_url (PR #18)
- [x] Upload files to workspace (drag-drop, click, clipboard paste)
- [x] File tray with remove button
- [x] Tool progress shown in activity bar above composer
@@ -252,8 +254,8 @@ Add more models. Group by provider. Model info tooltip on hover.
Both sidebar and workspace panel are drag-resizable with localStorage persistence.
### Sprint 3.3: Workspace File Actions
-- [ ] Rename file (inline, double-click) (Wave 3)
-- [ ] Create folder (Wave 3)
+- [x] Rename file (inline, double-click) (Sprint 14)
+- [x] Create folder (Sprint 14)
- [x] Syntax highlighted code preview (Prism.js)
### Sprint 3.4: Conversation Controls
@@ -316,3 +318,17 @@ Collapsible sidebar hamburger. Touch-friendly controls. Swipe gestures.
### Sprint 7.4: Performance and Scale
Virtual scroll for session/message lists. Incremental message loading.
+
+---
+
+## User Requested Features
+
+Community-requested enhancements tracked from GitHub issues.
+
+| Feature | Issue | Description | Complexity |
+|---------|-------|-------------|-----------|
+| Workspace tree view | #22 | Accordion/tree view for workspace file browser instead of flat list. Lazy-load subdirectories on expand, no backend changes needed. | Medium |
+| Docker container | #7 | Docker Compose setup with separate hermes-agent and hermes-webui containers, multi-arch (amd64 + arm64), volume mounts for config. | Medium-High |
+| Authentication | #23 | Password gate via `HERMES_WEBUI_PASSWORD` env var, login page, signed cookie. Already planned in Sprint 7.1. | Low-Medium |
+| Send key / personalization | #26 | Toggle send key (Enter vs Ctrl/Cmd+Enter) and queue vs interrupt mode as global settings. | Low |
+| Mobile responsive UI | #21 | Hamburger menu, slide-out sidebar drawer, touch-friendly controls. Already planned in Sprint 7.3. | Medium-High |
diff --git a/SPRINTS.md b/SPRINTS.md
index cca673b..1ec22df 100644
--- a/SPRINTS.md
+++ b/SPRINTS.md
@@ -1,6 +1,6 @@
# Hermes Web UI -- Forward Sprint Plan
-> Current state: v0.15 | 221 tests | Daily driver ready
+> Current state: v0.18 | 237 tests | Daily driver ready
> This document plans the path from here to two targets:
>
> Target A: 1:1 feature parity with the Hermes CLI (everything you can do from the
@@ -14,15 +14,17 @@
---
-## Where we are now (v0.12.1)
+## Where we are now (v0.18)
-**CLI parity: ~80% complete.** Core agent loop, all tools visible, workspace
-file ops, cron/skills/memory CRUD, session management, streaming, cancel --
-all solid. Gaps are configuration, subagent visibility, and runtime controls.
+**CLI parity: ~85% complete.** Core agent loop, all tools visible, workspace
+file ops, cron/skills/memory CRUD, session management, streaming, cancel,
+multi-provider models, custom endpoint discovery -- all solid. Gaps are
+subagent visibility, toolset control, and code execution.
-**Claude parity: ~55% complete.** Chat, streaming, file browser,
-session management, tool cards, syntax highlighting, model switching -- all
-present. Gaps are project organization, artifacts, voice, sharing, mobile.
+**Claude parity: ~65% complete.** Chat, streaming, file browser, session
+management, tool cards, syntax highlighting, model switching, projects,
+settings, Mermaid diagrams, mobile layout -- all present. Gaps are
+artifacts, voice, reasoning display, sharing.
---
@@ -425,7 +427,7 @@ address.
| Mermaid diagrams | Done (Sprint 14) |
| Projects / folders | Done (Sprint 15) |
| Pinned/starred sessions | Done (Sprint 12) |
-| Reasoning display | Sprint 16 |
+| Reasoning display | Sprint 18 |
| Voice input | Sprint 17 |
| TTS playback | Sprint 17 |
| Notifications | Done (Sprint 13) |
@@ -451,4 +453,4 @@ address.
*Last updated: April 2, 2026*
*Current version: v0.18 | 237 tests*
-*Next sprint: Sprint 17 (Slash Commands + Thinking Display)*
+*Next sprint: Sprint 17 (Voice + Multimodal Input)*