58 KiB
Hermes Web UI -- Changelog
Living document. Updated at the end of every sprint. Repository: https://github.com/nesquena/hermes-webui
[v0.34.1] Theme variable polish
April 5, 2026 | 433 tests
Bug Fixes
- All non-dark themes had broken surfaces, topbar, and dropdowns. 30+ hardcoded dark-navy rgba/hex values in style.css were stuck on the Dark palette regardless of active theme. Fixed by introducing 7 new CSS variables (
--surface,--topbar-bg,--main-bg,--input-bg,--hover-bg,--focus-ring,--focus-glow) defined per-theme, replacing every hardcoded reference. (#100)
[v0.34] Sprint 26 -- Pluggable UI Themes
April 5, 2026 | 433 tests
Features
- 6 built-in themes. Dark (default), Light, Slate, Solarized Dark, Monokai,
Nord. Defined as CSS variable overrides on
:root[data-theme="name"]— the entire UI adapts automatically. - Theme picker in Settings. Dropdown with instant live preview. Changes apply immediately as you click through options.
/themeslash command./theme dark,/theme light, etc.- Theme persistence. Saved server-side in
settings.jsonand client-side inlocalStoragefor flicker-free loading on page refresh. - Flash prevention. Inline
<script>in<head>reads localStorage before the stylesheet loads — no flash of the wrong theme. - Custom theme support. Any theme name is accepted (no enum gate). Create a
:root[data-theme="name"]CSS block and it works. SeeTHEMES.md. - Unsaved changes guard. Settings panel now tracks dirty state and shows a "You have unsaved changes" bar with Save/Discard buttons when closing with unpersisted changes. Theme preview reverts on discard.
Architecture
static/style.css: 6 theme blocks using CSS variable overrides. Light theme includes scrollbar and selection overrides.static/commands.js:/themecommand with validation.static/panels.js: Settings dirty tracking, revert-on-discard, unsaved bar.static/boot.js: Theme applied from server settings on boot.api/config.py:themefield in_SETTINGS_DEFAULTS(no enum gate).THEMES.md: Full documentation for creating custom themes.
Tests
- 9 new tests in
test_sprint26.py: default theme, round-trip persistence for all 6 built-in themes, custom theme acceptance, settings isolation. Total: 433 tests.
[v0.33] /insights Sync + state.db Bridge Fix
April 5, 2026 | 424 tests
Features
- Opt-in
/insightssync. New "Sync usage to /insights" setting (default: off). When enabled, after each turn the WebUI mirrors session token usage, cost, model, and title intostate.dbsohermes /insightsincludes browser session activity. (#92, #93)
Bug Fixes
- state_sync.py correctness fixes. Three bugs in the initial implementation caught during code review: wrong class name (
HermesState→SessionDB), wrong constructor argument type (str→Path), wrong title update method (_execute_writewith bad signature →set_session_title). Also fixed a SQLite connection leak (persistent connection opened per call, never closed). (#95)
[v0.32] Auto-Compaction Handling + /compact Command (Issue #90)
April 5, 2026 | 424 tests
Features
- Auto-compaction detection. When the agent's
run_conversation()triggers context compression and rotates the session ID, the WebUI detects the mismatch and renames the session file + cache entry so messages don't split across files. compressedSSE event. Frontend receives a notification when compression fires, shows a system message ("Context was auto-compressed") and a toast./compactslash command. Type/compactto request the agent compress the conversation context. Sends a natural-language message that triggers the agent's compression preflight.- Real context window data. The context usage indicator now uses actual
context_length,threshold_tokens, andlast_prompt_tokensfrom the agent's compressor instead of the client-side model name lookup. Tooltip shows the auto-compress threshold. Hides gracefully when the agent has no compressor.
Architecture
api/streaming.py: Session ID mismatch detection afterrun_conversation(), file rename, SESSIONS cache update under lock,compressedSSE event,context_length/threshold_tokens/last_prompt_tokensin usage dict.static/commands.js:/compactcommand.static/messages.js:compressedSSE event handler.static/ui.js:_syncCtxIndicator()rewritten to use server-side compressor data instead of client-side model estimates.
[v0.31.2] CLI session delete fix
April 5, 2026 | 424 tests
Bug Fixes
- CLI sessions could not be deleted from the sidebar. The delete handler only
removed the WebUI JSON session file, so CLI-backed sessions came back on refresh.
Added
delete_cli_session(sid)inapi/models.pyand call it from/api/session/deleteso the SQLitestate.dbrow and messages are removed too. (#87, #88)
Notes
- The public test suite still passes at 424/424.
- Issue #87 already had a comment confirming the root cause, so no new issue comment was needed here.
[v0.31] UI Polish + Deployment Hardening
April 4, 2026 | 424 tests
Bug Fixes
- Profile dropdown overlaps chat messages.
.topbarhad no stacking context, causing the dropdown to paint over.messages. Addedposition:relative;z-index:10to.topbar. (#71) - Workspace dropdown clipped by sidebar.
.sidebar overflow:hiddenswallowed the upward-opening workspace dropdown entirely. Changed tooverflow:visible(scroll lives on.session-list); addedposition:relative;z-index:10to.sidebar-bottom. (#71) - Slash-command autocomplete behind tool cards.
.composer-wraphadposition:relativebut noz-index, letting tool cards bleed over it. Addedz-index:10. (#71) - Skill picker clipped inside Settings modal.
.settings-panel overflow-y:autoclipped the absolute-positioned skill picker. Moved scroll to.settings-body, set panel tooverflow:visible, raised skill picker toz-index:1100. (#71) - CLI session badge blocks action buttons on hover. Added
.session-item.cli-session:hover::after { display:none }so the gold "cli" label hides on hover, making archive/delete/pin fully reachable. (#71) - Workspace dropdown name and path crowded on same line.
.ws-optwas a plain block with inline spans. Addedflex-direction:column;gap:4pxso name and path stack cleanly. (#71) - Both servers sharing same state directory.
api/config.pyandstart.shboth defaulted to~/.hermes/webui-mvp(an internal dev name). Changed default to~/.hermes/webui-- generic, appropriate for any deployment. Override withHERMES_WEBUI_STATE_DIR. (#72, #73)
[v0.30.1] CLI Session Bridge Fixes
April 4, 2026 | 424 tests
Bug Fixes
- CLI sessions not appearing in sidebar. Three frontend gaps:
sessions.jswasn't rendering CLI sessions (missingis_cli_sessioncheck in render loop), sidebar click handler didn't trigger import, and the "cli" badge CSS selector wasn't matching the rendered DOM structure. (#58) - CLI bridge read wrong profile's state.db.
get_cli_sessions()resolvedHERMES_HOMEat server launch time, not at call time. After a profile switch, it kept reading the original profile's database. Now resolves dynamically viaget_active_hermes_home(). (#59) - Silent SQL error swallowed all CLI sessions. The
sessionstable instate.dbhas noprofilecolumn — the query referenceds.profilewhich caused a silentOperationalError. Theexcept Exception: return []handler swallowed it, returning zero CLI sessions. Removed the column reference and added explicit column-existence checks. (#60)
Features
- "Show CLI sessions" toggle in Settings. New checkbox in the Settings panel
to show/hide CLI sessions in the sidebar. Persisted server-side in
settings.json(show_cli_sessions, defaulttrue). When disabled, CLI sessions are excluded from/api/sessionsresponses. (#61)
[v0.30] CLI Session Bridge (Community: @thadreber-web)
April 4, 2026 | 424 tests
Features
- CLI session bridge. The WebUI now reads sessions from the hermes-agent's
SQLite store (
state.db). CLI sessions appear in the sidebar with a gold "cli" indicator badge. Click to import into the WebUI store with full message history — replies then work through the normal agent pipeline. /api/session/import_cliendpoint. Imports a CLI session into the WebUI JSON store. Idempotent — returns existing session if already imported. Derives title from first message, inherits active profile and workspace./api/sessionsmerges CLI sessions. Sidebar shows both WebUI and CLI sessions sorted by last activity. Deduplication ensures WebUI sessions take priority when the same session_id exists in both stores.- CLI session fallback on
/api/session. If a session_id isn't found in the WebUI store, falls back to reading from the CLI SQLite store.
Architecture
api/models.py:get_cli_sessions(),get_cli_session_messages(),import_cli_session(). All use parameterized SQL queries andwithfor connection management. Graceful fallback on missing sqlite3 or state.db.api/routes.py: CLI fallback in GET/api/session, merged list in GET/api/sessions, POST/api/session/import_cli.static/style.css:.cli-sessionindicator styles (gold border + badge).
[v0.29] Sprint 23: Agentic Transparency + Polish
April 4, 2026 | 424 tests
Features
-
Token/cost display. Agent usage (input tokens, output tokens, estimated cost) is now read after each conversation and persisted on the session. A muted badge appears below the last assistant message when enabled. Off by default — toggle via the Settings panel checkbox or
/usageslash command. Persists server-side across refreshes. -
Subagent delegation cards.
subagent_progressevents now render with a 🔀 icon and a blue indented left border to visually distinguish child tool activity from parent tool calls.delegate_taskcards display as "Delegate task" with cleaner formatting. -
Skill picker in cron create form. The "New Job" form now has a search input + tag chip picker for attaching skills to cron jobs. Skills fetched from
/api/skills, filtered on keyup, added/removed as tag chips.submitCronCreate()sendsskillsarray in the POST body. Backend already supported the field — this was a pure frontend gap. -
Skill linked files viewer. Skill preview panel now renders a "Linked Files" section below SKILL.md content when a skill has
references/,templates/,scripts/, orassets/subdirectories. Clicking a file loads it in the preview panel with syntax highlighting. Newfilequery param onGET /api/skills/contentserves linked files with path traversal protection. -
Workspace tree state persists across refreshes. Expanded directory paths are saved to
localStoragekeyed by workspace path (hermes-webui-expanded:{path}). On every root load (page refresh, session switch), the saved state is restored and previously-expanded directories are pre-fetched so the tree renders fully on first paint. -
Timestamps fixed.
api/streaming.pynow stampstimestampon every message that lacks one at conversation completion. ThedoneSSE event also stamps_tson the last assistant message immediately. Timestamps were already rendered in the UI (Sprint 14, hover-to-reveal) but most messages had no timestamp field, so nothing ever showed. -
/usageslash command. Instant toggle for token usage display. Shows a toast, persists to server, updates the Settings checkbox if open, re-renders immediately.
Bug Fixes
-
XSS via inline onclick + esc(). Skill names and file paths embedded in
onclickHTML attributes usedesc()for encoding.esc()converts'to'(HTML-safe) but browsers decode it back before executing JS, allowing skill names with apostrophes to break out of string literals. Fixed by switching todata-*attributes +addEventListener. -
rglob wildcard injection. The
namequery param for/api/skills/content?file=was passed directly toSKILLS_DIR.rglob(), which accepts glob patterns.name=*would match an arbitrary directory and use it as the trust base for path traversal checking. Fixed by rejecting names containing* ? [ ]metacharacters with 400. -
_fmtTokens(null)returned "null".String(null)="null"would appear in the usage badge for sessions missing fields. Fixed with a!n || n < 0guard returning'0'. -
Usage badge on wrong row. Badge used
:last-childwhich could target a user message row. Fixed by addingdata-roleto message rows and scanning backwards for the lastassistantrow. -
Tool name resolution. Tool call entries in session JSON sometimes stored the literal string
"tool"as the name when the call ID couldn't be resolved. Fixed: defaults to empty string and skips unresolvable entries. -
Inline import inside loop.
import json as _j2inside the done-handler loop instreaming.pymoved to module-level.
Session Model
-
Added
input_tokens,output_tokens,estimated_costfields to Session (defaults: 0, 0, None). Included incompact(), session JSON, and all API responses. Backward-compatible via**kwargs. -
Added
argscapture totool_callssession JSON entries (truncated snapshot of tool inputs, up to 6 keys / 120 chars each).
Settings
- New
show_token_usageboolean setting (default:false). Stored insettings.json, loaded on boot alongsidesend_key.
Tests
- Renamed
test_sprint24.py→test_sprint23.py. - Strengthened session usage assertions (explicit field presence checks).
- Added: path traversal rejection test, wildcard name rejection test, cron create with skills array test.
- Total: 424 tests (up from 415).
[v0.28.1] CI Pipeline + Multi-Arch Docker Builds
April 3, 2026 | 426 tests
Features
- GitHub Actions CI. New workflow triggers on tag push (
v*). Builds multi-arch Docker images (linux/amd64 + linux/arm64), pushes toghcr.io/nesquena/hermes-webui, and creates a GitHub Release with auto-generated release notes. Uses GHA layer caching for fast rebuilds. - Pre-built container images. Users can now
docker pull ghcr.io/nesquena/hermes-webui:latestinstead of building locally.
[v0.27] Profile Creation Fallback for Docker (Issue #44)
April 3, 2026 | 426 tests
Bug Fixes
- Profile creation works without hermes-agent. In Docker containers where
hermes_cliis not importable, profile creation now falls back to a local implementation that creates the directory structure and optionally clones config files. Previously returnedRuntimeErrorwith "hermes-agent required". - Name validation uses
fullmatch(). Prevents trailing-newline bypass of the$anchor inre.match(). Not reachable from the web UI (name is stripped), but fixed for defense-in-depth. clone_fromvalidated increate_profile_api(). Defense-in-depth: prevents path traversal if called by a non-HTTP client.- Fallback return uses full 9-key schema. Previously returned only 2 keys
(
name,path), inconsistent with the normal response shape. - Atomic directory creation.
mkdir(exist_ok=False)prevents TOCTOU race on concurrent profile creates.
Architecture
api/profiles.py:_validate_profile_name(),_create_profile_fallback(),_PROFILE_ID_RE,_PROFILE_DIRS,_CLONE_CONFIG_FILESconstants matching upstreamhermes_cli.profiles.docker-compose.yml: Removed:rofrom~/.hermesmount (required for profile writes). Localhost-only binding preserved.
[v0.26] Profile System Polish -- 10 Post-Sprint-23 Fixes
April 3, 2026 | 426 tests
Bug Fixes
- Profile switch base dir bug. When
HERMES_HOMEwas mutated to aprofiles/subdir at startup,switch_profile()doubled the path (e.g.~/.hermes/profiles/X/profiles/X). New_resolve_base_hermes_home()detects profile subdirs and walks up to the actual base. - Cross-provider model routing. Picking a model from a different provider than the config's default now routes through OpenRouter instead of trying a direct API call to a provider whose key may not exist.
- Legacy sessions missing profile tag.
all_sessions()now backfillsprofile='default'for pre-Sprint-22 sessions so the profile filter works. - Workspace list cleanup. Stale paths, test artifacts, and cross-profile entries are now cleaned on load. Legacy global workspace file migrated once for the default profile.
- API error messages.
api()helper now parses JSON error bodies and surfaces the human-readable message instead of raw JSON. - Workspace dropdown moved to sidebar. The workspace picker now opens upward from the sidebar bottom instead of clipping behind the topbar.
Features
- Rate limit error display. Rate limit errors (429) now show a distinct card with a rate limit icon and hint, instead of the generic error message.
- SSE
apperror/warningevents. Server can send typed error events that the frontend handles with appropriate UX (rate limit card, fallback notice, etc.). - Smart model resolver.
_findModelInDropdown()handles name mismatches between config model IDs and dropdown values (e.g.claude-sonnet-4-6vsanthropic/claude-sonnet-4.6). - Profile switch starts new session. When the current session has messages, switching profiles automatically starts a fresh session to prevent cross-profile tagging.
- Per-profile toolsets. Agent now reads
platform_toolsets.clifrom the active profile's config at call time, not the boot-time snapshot. - Per-profile fallback model.
fallback_modelconfig is read from the active profile and passed to AIAgent.
Architecture
api/profiles.py:_resolve_base_hermes_home()replaces naive env var read.api/workspace.py:_clean_workspace_list(),_migrate_global_workspaces().api/streaming.py: Per-profile toolsets and fallback model at call time.api/models.py:all_sessions()backfillsprofile='default'.static/ui.js:_findModelInDropdown(),_applyModelToDropdown().static/messages.js:apperrorandwarningSSE event handlers.
[v0.25] Sprint 23 -- Profile/Workspace/Model Coherence
April 3, 2026 | 423 tests
Features
- Profile-local workspace storage. Each named profile now stores its own
workspaces.jsonandlast_workspace.txtunder{profile_home}/webui_state/. Default profile continues using the global STATE_DIR for backward compat. - Profile switch returns defaults.
POST /api/profile/switchresponse now includesdefault_modelanddefault_workspacefrom the new profile's config.yaml, enabling one-round-trip state sync. - Session profile filter. Session sidebar filters to the active profile by default. "Show N from other profiles" toggle reveals sessions from all profiles, modeled on the existing archived toggle. Resets on profile switch.
Bug Fixes
- Model picker ignores profile on switch.
switchToProfile()now clears thehermes-webui-modellocalStorage key so the profile's default model applies instead of a stale preference from another profile. - Workspace list was global. Switching profiles no longer shows the wrong profile's workspaces.
DEFAULT_WORKSPACEwas a boot-time singleton. Now resolved dynamically through_profile_default_workspace().- Session list showed all profiles. Now filtered to active profile.
switchToProfile()didn't refresh workspaces or sessions. Now refreshes workspace list, session list, and resets profile filter on switch.
Architecture
api/workspace.pyrewritten with profile-aware path resolution.api/profiles.py:switch_profile()returnsdefault_modelanddefault_workspace.static/sessions.js: Profile filter with toggle UI.static/panels.js: Full cascade refresh on profile switch.- 8 new tests in
test_sprint23.py.
[v0.24] Sprint 22 -- Multi-Profile Support (Issue #28)
April 3, 2026 | 415 tests
Features
- Profile picker (topbar). Purple-accented chip with SVG user icon. Click to open dropdown listing all profiles with gateway status dots (green = running), model info, and skill count. Click any profile to switch; "Manage profiles" link opens the sidebar panel.
- Profiles management panel. New sidebar tab with full CRUD UI. Profile cards show name, model/provider, skill count, API key status, and gateway status badge. "Use" button switches profile, delete button removes non-default profiles (with confirmation).
- Profile creation. "+ New profile" form with name validation (
[a-z0-9_-]), optional "clone config from active" checkbox. Wraps the CLI'shermes_cli.profiles.create_profile(). - Profile deletion. Confirm dialog. Auto-switches to default if deleting the active profile. Blocked while agent is running.
- Seamless profile switching. No server restart. Profile switch updates
HERMES_HOME, patches module-level caches in hermes-agent'sskills_toolandcron/jobs, reloads.envAPI keys andconfig.yaml, refreshes the model dropdown, skills, memory, and cron panels. - Per-session profile tracking.
profilefield on Session records which profile was active at creation. Backward-compatible (nullfor old sessions).
Bug Fixes
- Hardcoded
~/.hermespaths. Memory read/write and model discovery used hardcoded paths. Now resolved throughget_active_hermes_home(). - Module-level path caching. hermes-agent modules snapshot
HERMES_HOMEat import time. Profile switch now monkey-patchesSKILLS_DIR,CRON_DIR,JOBS_FILE,OUTPUT_DIRso they track the active profile.
Architecture
- New
api/profiles.py: profile state management wrappinghermes_cli.profiles. Thread-safe (_profile_lock). Lazy imports avoid circular deps. api/config.py: module-levelcfgreplaced with reloadableget_config()/reload_config(). Dynamic_get_config_path()resolves through profile.api/streaming.py:HERMES_HOMEadded to env save/restore block.- Profile switch blocked while agent streams are active.
- 5 new API endpoints:
GET /api/profiles,GET /api/profile/active,POST /api/profile/switch,POST /api/profile/create,POST /api/profile/delete. - Zero modifications to hermes-agent code.
[v0.23] Sprint 21 -- Mobile Responsive + Docker
April 3, 2026 | 415 tests
Features
- Mobile responsive layout (Issue #21). Full mobile experience with
hamburger sidebar (slide-in overlay), bottom navigation bar (5-tab iOS
pattern), and files slide-over panel. Touch targets minimum 44px. Composer
positioned above bottom nav. Session clicks auto-close sidebar. Desktop
layout completely unchanged — all mobile elements hidden via
@media. - Docker support (Issue #7). Dockerfile (
python:3.12-slim), docker-compose.yml with named volume for state persistence, optional~/.hermesmount for agent features. Binds to127.0.0.1by default for security.
Bug Fixes (from review)
- CSS cascade broke mobile slide-in.
position:relativerules after the media query overrodeposition:fixedon mobile. Wrapped in@media(min-width:641px). - mobileSwitchPanel() always reopened sidebar. Chat tab now closes sidebar instead of reopening it over the main chat area.
- Dockerfile missing pip install. Added
pip install -r requirements.txt. - No .dockerignore. Added exclusions for
.git,tests/,.env*. - docker-compose tilde expansion. Changed
~/.hermesdefault to${HOME}/.hermes(Docker Compose doesn't shell-expand~).
Architecture
- Mobile navigation functions in
boot.js:toggleMobileSidebar(),closeMobileSidebar(),toggleMobileFiles(),mobileSwitchPanel(). sessions.js:closeMobileSidebar()called after session click.- 69 new CSS lines in
@media(max-width:640px)block. - New files:
Dockerfile,docker-compose.yml,.dockerignore.
[v0.22] Sprint 20 -- Voice Input + Send Button Polish
April 3, 2026 | 415 tests
Features
- Voice input via Web Speech API. Microphone button in the composer. Tap to start recording, tap again (or send) to stop. Live interim transcription appears in the textarea. Auto-stops after ~2s of silence. Final text stays editable before sending. Appends to existing textarea content rather than replacing it. Button hidden when browser doesn't support Web Speech API. No API keys, no external libraries, no server changes. Works in Chrome, Edge, Safari (partial). Firefox unsupported (button stays hidden).
- Send button polish. Send button redesigned as a 34px icon-only circle with upward arrow SVG. Hidden by default — appears with pop-in spring animation when textarea has content or files are attached. Disappears on send or when content is cleared. Hidden while agent is responding. Blue fill (#7cb9ff) with glow, scale hover/active for tactile feedback.
Architecture
- Voice input IIFE in
boot.js: SpeechRecognition lifecycle withcontinuous=false,interimResults=true, error handling viashowToast(). _prefixvariable snapshots existing textarea content on recording start so dictation appends rather than overwrites.btnSend.onclickstops active recognition before sending (send guard).- CSS:
.mic-btn,.mic-btn.recording(red pulse),.mic-status,.mic-dot,@keyframes mic-pulse. updateSendBtn()inui.jstracks textarea content, pending files, and busy state. Hooked intosetBusy(),renderTray(),autoResize(), and input event listener.- CSS:
.send-btnredesigned (circle, glow),.send-btn.visible+@keyframes send-pop-in(spring animation).
Tests
- 52 new tests in
test_sprint20.py: voice input HTML, CSS, JS, append behaviour, error handling, regressions. - 33 new tests in
test_sprint20b.py: send button HTML, CSS, JS, animation, visibility logic, regressions. Total: 415 tests.
[v0.21] Sprint 19 -- Auth + Security Hardening
April 3, 2026 | 328 tests
Features
- Password authentication (Issue #23). Optional password auth, off by default.
Enable via
HERMES_WEBUI_PASSWORDenv var or Settings panel. Password-only (single-user app). Signed HMAC HTTP-only cookie with 24h TTL. Minimal dark-themed login page at/login. API calls without auth return 401; page loads redirect. Newapi/auth.pymodule with hashing, verification, session management. - Security headers. All responses now include
X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Referrer-Policy: same-origin. - POST body size limit. Non-upload POST bodies capped at 20MB via
read_body(). - Settings panel additions. "Access Password" field and "Sign Out" button (only visible when auth is active).
Architecture
- New
api/auth.py: password hashing (SHA-256 + STATE_DIR salt), signed cookies, auth middleware, public path allowlist. - Auth check in
server.pydo_GET/do_POST before routing. password_hashadded to_SETTINGS_DEFAULTS.
Tests
- 10 new tests in
test_sprint19.py: auth status, login flow, security headers, cache-control, settings password field, request size limit. Total: 328 tests (328 passing).
[v0.20] Sprint 18 -- File Preview Auto-Close + Thinking Display + Workspace Tree
April 3, 2026 | 318 tests
Features
- File preview auto-close on directory navigation. When viewing a file in
the right panel and navigating directories (breadcrumbs, up button, folder
clicks), the preview now automatically closes instead of showing stale
content.
clearPreview()extracted as named function and called fromloadDir(). Unsaved preview edits prompt for confirmation before discarding. - Thinking/reasoning display. Assistant messages with structured content
arrays containing
type:'thinking'ortype:'reasoning'blocks (Claude extended thinking, o3 reasoning) now render as collapsible gold-themed cards above the response text. Collapsed by default. Click the header to expand and see the model's reasoning process. Usesesc()on all content for XSS safety. - Workspace tree view (Issue #22). Directories expand/collapse in-place
with toggle arrows. Single-click toggles a directory open/closed. Double-click
navigates into it (breadcrumb view). Subdirectory contents fetched lazily from
the API and cached in
S._dirCache. Nesting depth shown via indentation. Empty directories show "(empty)" placeholder. Breadcrumb navigation still works alongside the tree view.
Bug Fixes
- Stale tree cache on session switch.
S._dirCacheandS._expandedDirsare now cleared when navigating to the root directory, preventing session B from showing session A's cached file listings. - clearPreview() discards unsaved edits. Navigation now checks
_previewDirtyand prompts before discarding unsaved preview changes.
Architecture
clearPreview()extracted from inline handler to named function inboot.js.- Thinking card styles added to
style.css(gold-themed, collapsible). - Tree toggle and empty-directory styles added to
style.css.
[v0.19] Sprint 17 -- Workspace Polish + Slash Commands + Settings
April 3, 2026 | 318 tests
Features
- Workspace breadcrumb navigation. Clicking into subdirectories now shows a
breadcrumb path bar (e.g.
~ / src / components) with clickable segments to navigate back. An "up" button appears in the panel header when inside a subdirectory. File operations (rename, delete, new file/folder) stay in the current directory instead of jumping back to root. Foundation for Issue #22 (tree view). - Slash commands. Type
/in the composer to see an autocomplete dropdown of built-in commands. Newcommands.jsmodule with command registry. Built-in commands:/help,/clear,/model <name>,/workspace <name>,/new. Arrow keys navigate, Tab/Enter select, Escape closes. Unrecognized commands pass through to the agent normally. - Send key setting (Issue #26). New setting in Settings panel to choose
between Enter (default) and Ctrl/Cmd+Enter as the send key. Persisted to
settings.jsonvia the existing settings API. Setting loads on boot. Server-side validation ensures only valid values (enter,ctrl+enter).
Architecture
- New
static/commands.jsmodule (7th JS module): command registry, parser, autocomplete dropdown, and built-in command handlers. send_keyadded to_SETTINGS_DEFAULTSinapi/config.pywith enum validation (_SETTINGS_ENUM_VALUESrejects unknown values server-side).S.currentDirstate tracking added toui.jsfor workspace navigation.
Tests
- 6 new tests in
test_sprint17.py: send_key default, round-trip save with cleanup, invalid value rejection, unknown key ignored, commands.js served, workspace root listing. Total: 318 passed.
[v0.18.1] Safe HTML Rendering + Sprint 16 Tests
April 2, 2026 | 289 tests
Features
- Safe HTML rendering in AI responses. AI models sometimes emit HTML tags
(
<strong>,<em>,<code>,<br>) in their responses. Previously these showed as literal escaped text. A new pre-pass inrenderMd()converts safe HTML tags to markdown equivalents before the pipeline runs. Code blocks and backtick spans are stashed first so their content is never touched. inlineMd()helper. New function for processing inline formatting inside list items, blockquotes, and headings. The old code calledesc()directly, which escaped tags that had already been converted by the pre-pass.- Safety net. After the full pipeline, any HTML tags not in the output
allowlist (
SAFE_TAGS) are escaped viaesc(). XSS fully blocked -- 7 attack vectors tested. - Active session gold style. Active session uses gold/amber (
#e8a030) instead of blue, matching the logo gradient. Project border-left skipped when active (gold always wins).
Tests
- 74 new tests in
test_sprint16.py: static analysis (6), behavioral (10), exact regression (1), XSS security (7), edge cases (51). Total: 289 passed.
[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-levelICONSconstant insessions.js. - Action buttons overlay. All session action buttons (pin, move, archive,
duplicate, trash) wrapped in a
.session-actionscontainer withposition: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-projectpersistent 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 endpointexceptblock inconfig.py, butloggerwas never imported. Every failed custom endpoint fetch crashed withNameError, returning HTTP 500 for/api/models. Replaced with silentpasssince unreachable endpoints are expected. (PR #24) - Project picker clipping and width. Picker was clipped by
overflow:hiddenon ancestor elements. Width calculation improved with dynamic sizing (min 160px, max 220px). Event listenerclosehandler moved after DOM append to fix reference-before-definition. Reorderedpicker.remove()beforeremoveEventListenerfor correct cleanup. (PR #25)
[v0.17.2] Model Update
April 2, 2026
Enhancements
- GLM-5.1 added to Z.AI model list. New model available in the dropdown for Z.AI provider users. (Fixes #17)
[v0.17.1] Security + Bug Fixes
April 2, 2026 | 237 tests
Security
- Path traversal in static file server.
_serve_static()now sandboxes resolved paths insidestatic/via.relative_to(). PreviouslyGET /static/../../.hermes/config.yamlcould expose API keys. - XSS in markdown renderer. All captured groups in bold, italic, headings,
blockquotes, list items, table cells, and link labels now run through
esc()beforeinnerHTMLinsertion. - Skill category path traversal. Category param validated to reject
/and..to prevent writing outside~/.hermes/skills/. - Debug endpoint locked to localhost.
/api/approval/inject_testreturns 404 to any non-loopback client. - CDN resources pinned with SRI hashes. PrismJS and Mermaid tags now have
integrity+crossoriginattributes. Mermaid pinned to@10.9.3. - Project color CSS injection. Color field validated against
^#[0-9a-fA-F]{3,8}$to preventstyle.backgroundinjection. - Project name length limit. Capped at 128 chars, empty-after-strip rejected.
Bug Fixes
- OpenRouter model routing regression.
resolve_model_provider()was incorrectly stripping provider prefixes from OpenRouter model IDs (e.g.openai/gpt-5.4-minibecamegpt-5.4-miniwith provideropenai), causing AIAgent to look for OPENAI_API_KEY and crash. Fix: only strip prefix whenconfig.providerexplicitly matches that direct-API provider. - Project picker invisible. Dropdown was clipped by
.session-itemoverflow:hidden. Now appended todocument.bodywithposition:fixed. - Project picker stretched full width. Added
max-width:220px; width:max-contentto constrain the fixed-positioned picker. - No way to create project from picker. Added "+ New project" item at the bottom of the picker dropdown.
- Folder button undiscoverable. Now shows persistently (blue, 60% opacity) when session belongs to a project.
- Picker event listener leak.
removeEventListeneradded to all picker item onclick handlers. - Redundant sys.path.insert calls removed. Two cron handler imports no longer prepend the agent dir (already on sys.path via config.py).
[v0.17] Sprint 15 -- Session Projects + Code Copy + Tool Card Toggle
April 1, 2026 | 237 tests
Features
- Session projects. Named groups for organizing sessions. A project filter
bar (subtle chips) sits between the search input and the session list. Each
project has a name and color. Click a chip to filter; "All" shows everything.
Create inline (+), rename (double-click), delete (right-click). Assign sessions
via folder icon button with dropdown picker. Projects stored in
projects.json. Session model gainsproject_idfield. 5 new API endpoints. - Code block copy button. Every code block gets a "Copy" button in the language header bar (or top-right for plain blocks). Click copies to clipboard, shows "Copied!" for 1.5s.
- Tool card expand/collapse. When a message has 2+ tool cards, "Expand all / Collapse all" toggle appears above the card group.
[v0.16.2] Model List Updates + base_url Passthrough
April 1, 2026 | 247 tests
Bug Fixes
- MiniMax model list updated. Replaced stale ABAB 6.5 models with current MiniMax-M2.7, M2.7-highspeed, M2.5, M2.5-highspeed, M2.1 lineup matching hermes-agent upstream. (Fixes #6)
- Z.AI/GLM model list updated. Replaced GLM-4 series with current GLM-5, GLM-5 Turbo, GLM-4.7, GLM-4.5, GLM-4.5 Flash lineup.
- base_url passthrough to AIAgent.
resolve_model_provider()now readsbase_urlfrom config.yaml and passes it to AIAgent, so providers with custom endpoints (MiniMax, Z.AI, local LLMs) route to the correct API.
[v0.16.1] Community Fixes -- Mobile + Auth + Provider Routing
April 1, 2026 | 247 tests
Community contributions from @deboste, reviewed and refined.
Bug Fixes
- Mobile responsive layout. Comprehensive
@media(max-width:640px)rules for topbar, messages, composer, tool cards, approval cards, and settings modal. Uses100dvhwith100vhfallback to fix composer cutoff on mobile browsers. Textareafont-size:16pxprevents iOS/Android auto-zoom on focus. - Reverse proxy basic auth support. All
fetch()andEventSourceURLs now constructed vianew URL(path, location.origin)to strip embedded credentials per Fetch spec.credentials:'include'on fetch,withCredentials:trueon EventSource ensure auth headers are forwarded through reverse proxies. - Model provider routing. New
resolve_model_provider()helper inapi/config.pystrips provider prefix from dropdown model IDs (e.g.anthropic/claude-sonnet-4.6→claude-sonnet-4.6) and passes the correctproviderto AIAgent. Handles cross-provider selection by matching against known direct-API providers.
[v0.16] Sprint 14 -- Visual Polish + Workspace Ops + Session Organization
March 30, 2026 | 233 tests
Features
- Mermaid diagram rendering. Code blocks tagged
mermaidrender as diagrams inline. Mermaid.js loaded lazily from CDN on first encounter. Dark theme with matching colors. Falls back to code block on parse error. - Message timestamps. Subtle HH:MM time next to each role label. Full
date/time on hover tooltip. User messages get
_tsfield when sent. - File rename. Double-click any filename in workspace panel to rename
inline.
POST /api/file/renameendpoint with path traversal protection. - Folder create. Folder icon button in workspace panel header. Prompt
for name,
POST /api/file/create-direndpoint. - Session tags. Add
#tagto session titles. Tags shown as colored chips in sidebar. Click a tag to filter the session list. - Session archive. Archive icon on each session. Archived sessions
hidden by default; "Show N archived" toggle at top of list. Backend
POST /api/session/archivewitharchivedfield on Session model.
Bug Fixes
- Date grouping fix. Session list groups (Today/Yesterday/Earlier) now
use
created_atinstead ofupdated_at, preventing sessions from jumping between groups when auto-titling touchesupdated_at.
[v0.15] Sprint 13 -- Alerts + Session QoL + Polish
March 30, 2026 | 221 tests
Features
- Cron completion alerts. New
GET /api/crons/recentendpoint. UI polls every 30s (pauses when tab is hidden). Toast notification per completion with status icon. Red badge count on Tasks nav tab, cleared when tab is opened. - Background agent error alerts. When a streaming session errors out and the user is viewing a different session, a persistent red banner appears above the messages: "Session X has encountered an error." View button navigates, Dismiss clears.
- Session duplicate. Copy icon on each session in the sidebar (visible on hover). Creates a new session with the same workspace and model, titled "(copy)".
- Browser tab title.
document.titleupdates to show the active session title (e.g. "My Task -- Hermes"). Resets to "Hermes" when no session is active.
Bug Fixes
- Click guard added for duplicate button to prevent accidental session navigation.
[v0.14] Sprint 12 -- Settings Panel + Reliability + Session QoL
March 30, 2026 | 211 tests
Features
- Settings panel. Gear icon in topbar opens slide-in overlay. Persist default
model and workspace server-side in
settings.json. Server reads on startup. - SSE auto-reconnect. When EventSource drops mid-stream, attempts one reconnect
using the same stream_id after 1.5s. Shared
_wireSSE()function eliminates handler duplication. - Pin sessions. Star icon on each session. Pinned sessions float to top of sidebar under a gold "Pinned" header. Persisted in session JSON.
- Import session from JSON. Upload button in sidebar. Creates new session with fresh ID from exported JSON file.
Bug Fixes
models.pyuses_cfg.DEFAULT_MODELmodule reference sosave_settings()changes take effect fornew_session().- Full-scan fallback sort in
all_sessions()now accounts for pinned sessions. save_settings()whitelists known keys only, rejecting arbitrary data.- Escape key closes settings overlay.
[v0.13] Sprint 11 -- Multi-Provider Models + Streaming Smoothness
March 30, 2026 | 201 tests
Features
- Multi-provider model support. New
GET /api/modelsendpoint discovers configured providers fromconfig.yaml,auth.json, and API key environment variables. The model dropdown now populates dynamically from whatever providers the user has set up (Anthropic, OpenAI, Google, DeepSeek, Nous Portal, OpenRouter, etc.). Falls back to the hardcoded OpenRouter list when no providers are detected. Sessions with unlisted models auto-add them to the dropdown. - Smooth scroll pinning. During streaming, auto-scroll only when the user is near the bottom of the message area. If the user scrolls up to read earlier content, new tokens no longer yank them back down. Pinning resumes when they scroll back to the bottom.
Architecture
- Routes extracted to api/routes.py. All 49 GET/POST route handlers moved from server.py
into
api/routes.py(802 lines). server.py is now a 76-line thin shell: Handler class with structured logging, dispatch tohandle_get()/handle_post(), andmain(). Completes the server split started in Sprint 10. - Cleaned up duplicate dead-code routes that existed in the old
do_GET(skills/save, skills/delete, memory/write were duplicated in both GET and POST handlers).
Bug Fixes
- Regression tests updated for new route module structure.
[v0.12.2] Concurrency + Correctness Sweeps
March 31, 2026 | 190 tests
Two systematic audits of all concurrent multi-session scenarios. Each finding became a regression test so it cannot silently return.
Sweep 1 (R10-R12)
- R10: Approval response to wrong session.
respondApproval()usedS.session.session_id-- whoever you were viewing. If session A triggered a dangerous command requiring approval and you switched to B then clicked Allow, the approval went to B's session_id. Agent on A stayed stuck. Fixed: approval events tag_approvalSessionId;respondApproval()uses that. - R11: Activity bar showed cross-session tool status. Session A's tool
name appeared in session B's activity bar while you were viewing B. Fixed:
setStatus()in the tool SSE handler is now inside theactiveSidguard. - R12: Live tool cards vanished on switch-away and back. Switching back to
an in-flight session showed empty live cards even though tools had fired.
Fixed:
loadSession()INFLIGHT branch now restores cards fromS.toolCalls.
Sweep 2 (R13-R15)
- R13: Settled tool cards never rendered after response completes.
renderMessages()has a!S.busyguard on tool card rendering. It was called withS.busy=truein the done handler -- tool cards were skipped every time. Fixed:S.busy=falseset inline beforerenderMessages(). - R14: Wrong model sent for sessions with unlisted model.
send()used$('modelSelect').valuewhich could be stale if the session's model isn't in the dropdown. Fixed: now usesS.session.model || $('modelSelect').value. - R15: Stale live tool cards in new sessions.
newSession()didn't callclearLiveToolCards(). Fixed.
[v0.12.1] Sprint 10 Post-Release Fixes
March 31, 2026 | 177 tests
Critical regressions introduced during the server.py split, caught by users and fixed immediately.
uuidnot imported in server.py --chat/startreturned 500 (NameError) on every new messageAIAgentnot imported in api/streaming.py -- agent thread crashed immediately, SSE returned 404has_pendingnot imported in api/streaming.py -- NameError during tool approval checksSession.__init__missingtool_callsparam -- 500 on any session with tool history- SSE loop did not break on
cancelevent -- connection hung after cancel - Regression test file added (
tests/test_regressions.py): 10 tests, one per introduced bug. These form a permanent regression gate so each class of error can never silently return.
[v0.12] Sprint 10 -- Server Health + Operational Polish
March 31, 2026 | 167 tests
Post-sprint Bug Fixes
- SSE loop now breaks on
cancelevent (was hanging after cancel) setBusy(false)now always hides the Cancel buttonS.activeStreamIdproperly initialized in the S global state object- Tool card "Show more" button uses data attributes instead of inline JSON.stringify (XSS/parse safety)
- Version label updated to v0.2
Session.__init__accepts**kwargsfor forward-compatibility with future JSON fields- Test cron jobs now isolated via
HERMES_HOMEenv var in conftest (no more pollution of real jobs.json) last_workspacereset after each test in conftest (prevents workspace state bleed between tests)- Tool cards now grouped per assistant turn instead of piled before last message
- Tool card insertion uses
data-msg-idxattribute correctly (wasmsgIdx, matching HTML5 dataset API)
Architecture
- server.py split into api/ modules. 1,150 lines -> 673 lines in server.py.
Extracted modules:
api/config.py(101),api/helpers.py(57),api/models.py(114),api/workspace.py(77),api/upload.py(77),api/streaming.py(187). server.py is now the thin routing shell only. All business logic is independently importable.
Features
- Background task cancel. Red "Cancel" button appears in the activity bar while a task
is running. Calls
GET /api/chat/cancel?stream_id=X. The agent thread receives a cancel event, emits a 'cancel' SSE event, and the UI shows "Task cancelled." in the conversation. Note: a tool call already in progress (e.g. a long terminal command) completes before the cancel takes effect -- same behavior as CLI Ctrl+C. - Cron run history viewer. Each job in the Tasks panel now has an "All runs" button. Click to expand a list of up to 20 past runs with timestamps, each collapsible to show the full output. Click again to hide.
- Tool card UX polish. Three improvements:
- Pulsing blue dot on cards for in-progress tools (distinct from completed cards)
- Smart snippet truncation at sentence boundaries instead of hard byte cutoff
- "Show more / Show less" toggle on tool results longer than 220 chars
[v0.11] Sprint 9 -- Codebase Health + Daily Driver Gaps
March 31, 2026 | 149 tests
The sprint that closed the last gaps for heavy agentic use.
Architecture
- app.js replaced by 6 modules.
app.jsis deleted. The browser now loads 6 focused files:ui.js(530),workspace.js(132),sessions.js(189),messages.js(221),panels.js(555),boot.js(142). The modules are a superset of the original app.js (two functions --loadTodos,toolIcon-- were added directly to the modules after the split). No single file exceeds 555 lines.
Features
- Tool call cards inline. Every tool Hermes uses now appears as a collapsible card in the conversation between the user message and the response. Live during streaming, restored from session history on reload. Shows tool name, preview, args, result snippet.
- Attachment metadata persists on reload. File badges on user messages survive page refresh. Server stores filenames on the user message in session JSON.
- Todo list panel. New checkmark tab in the sidebar. Shows current task list parsed from the most recent todo tool result in message history. Status icons: pending (○), in-progress (◉), completed (✓), cancelled (✗). Auto-refreshes when panel is active.
- Model preference persists. Last-used model saved to localStorage. Restored on page load. New sessions inherit it automatically.
Bug Fixes
- Tool card toggle arrow only shown when card has expandable content
- Attachment tagging matches by message content to avoid wrong-turn tagging
- SSE tool event was missing
argsfield /api/sessionGET was not returningtool_calls(history lost on reload)
[v0.10] Sprint 8 -- Daily Driver Finish Line
March 31, 2026 | 139 tests
Features
- Edit user message + regenerate. Hover any user bubble, click the pencil icon. Inline textarea, Enter submits, Escape cancels. Truncates session at that point and re-runs.
- Regenerate last response. Retry icon on the last assistant bubble only.
- Clear conversation. "Clear" button in topbar. Wipes messages, keeps session slot.
- Syntax highlighting. Prism.js via CDN (deferred). Python, JS, bash, JSON, SQL and more.
Bug Fixes
- Reconnect banner false positive on normal loads (90-second window)
- Session list clipping on short screens
- Favicon 404 console noise (server now returns 204)
- Edit textarea auto-resize on open
- Send button guard while inline edit is active
- Escape closes dropdown, clears search, cancels active edit
- Approval polling not restarted on INFLIGHT session switch-back
- Version label updated to v0.10
Hotfix: Message Queue + INFLIGHT
- Message queue. Sending while busy queues the message with toast + badge. Drains automatically on completion. Cleared on session switch.
- Message stays visible on switch-away/back. loadSession checks INFLIGHT before server fetch, so sent message and thinking dots persist correctly.
[v0.9] Sprint 7 -- Wave 2 Core: CRUD + Search
March 31, 2026 | 125 tests
Features
- Cron edit + delete. Inline edit form per job, save and delete with confirmation.
- Skill create, edit, delete. "+ New skill" form in Skills panel. Writes to
~/.hermes/skills/. - Memory inline edit. "Edit" button opens textarea for MEMORY.md. Saves via
/api/memory/write. - Session content search. Filter box searches message text (up to 5 messages per session) in addition to titles. Debounced API call, results appended below title matches.
Architecture
/healthnow returnsactive_streamsanduptime_secondsgit initon<repo>/, pushed to GitHub
Bug Fixes
- Activity bar overlap on short viewports
- Model chip stale after session switch
- Cron output overflow in tasks panel
[v0.8] Sprint 6 -- Polish + Phase E Complete
March 31, 2026 | 106 tests
Architecture
- Phase E complete. HTML extracted to
static/index.html. server.py now pure Python. Line count progression: 1778 (Sprint 1) → 1042 (Sprint 5) → 903 (Sprint 6). - Phase D complete. All endpoints validated with proper 400/404 responses.
Features
- Resizable panels. Sidebar and workspace panel drag-resizable. Widths persisted to localStorage.
- Create cron job from UI. "+ New job" form in Tasks panel with name, schedule, prompt, delivery.
- Session JSON export. Downloads full session as JSON via "JSON" button in sidebar footer.
- Escape from file editor. Cancels inline file edit without saving.
[v0.7] Sprint 5 -- Phase A Complete + Workspace Management
March 30, 2026 | 86 tests
Architecture
- Phase A complete. JS extracted to
static/app.js. server.py: 1778 → 1042 lines. - LRU session cache.
collections.OrderedDictwith cap of 100, oldest evicted automatically. - Session index.
sessions/_index.jsonfor O(1) session list loads. - Isolated test server. Port 8788 with own state dir, conftest autouse cleanup.
Features
- Workspace management panel. Add/remove/rename workspaces. Persisted to
workspaces.json. - Topbar workspace quick-switch. Dropdown chip lists all workspaces, switches on click.
- New sessions inherit last workspace.
last_workspace.txttracks last used. - Copy message to clipboard. Hover icon on each bubble with checkmark confirmation.
- Inline file editor. Preview any file, click Edit to modify, Save writes to disk.
[v0.6] Sprint 4 -- Relocation + Session Power Features
March 30, 2026 | 68 tests
Architecture
- Source relocated to
<repo>/outside the hermes-agent git repo. Safe fromgit pull,git reset,git stash. Symlink maintained athermes-agent/webui-mvp. - CSS extracted (Phase A start). All CSS moved to
static/style.css. - Per-session agent lock (Phase B). Prevents concurrent requests to same session from corrupting environment variables.
Features
- Session rename. Double-click any title in sidebar to edit inline. Enter saves, Escape cancels.
- Session search/filter. Live client-side filter box above session list.
- File delete. Hover trash icon on workspace files. Confirm dialog.
- File create. "+" button in workspace panel header.
[v0.5] Sprint 3 -- Panel Navigation + Feature Viewers
March 30, 2026 | 48 tests
Features
- Sidebar panel navigation. Four tabs: Chat, Tasks, Skills, Memory. Lazy-loads on first open.
- Tasks panel. Lists scheduled cron jobs with status badges. Run now, Pause, Resume. Shows last run output automatically.
- Skills panel. All skills grouped by category. Search/filter. Click to preview SKILL.md.
- Memory panel. Renders MEMORY.md and USER.md as formatted markdown with timestamps.
Bug Fixes
- B6: New session inherits current workspace
- B10: Tool events replace thinking dots (not stacked alongside)
- B14: Cmd/Ctrl+K creates new chat from anywhere
[v0.4] Sprint 2 -- Rich File Preview
March 30, 2026 | 27 tests
Features
- Image preview. PNG, JPG, GIF, SVG, WEBP displayed inline in workspace panel.
- Rendered markdown.
.mdfiles render as formatted HTML in the preview panel. - Table support. Pipe-delimited markdown tables render as HTML tables.
- Smart file icons. Type-appropriate icons by extension in the file tree.
- Preview path bar with type badge. Colored badge shows file type.
[v0.3] Sprint 1 -- Bug Fixes + Foundations
March 30, 2026 | 19 tests
The first sprint. Established the test suite, fixed critical bugs.
Bug Fixes
- B1: Approval card now shows pattern keys
- B2: File input accepts valid types only
- B3: Model chip label correct for all 10 models (replaced substring check with dict)
- B4/B5: Reconnect banner on mid-stream reload (localStorage inflight tracking)
- B7: Session titles no longer overflow sidebar
- B9: Empty assistant messages no longer render as blank bubbles
- B11:
/api/sessionGET returns 400 (not silent session creation) when ID missing
Architecture
- Thread lock on SESSIONS dict
- Structured JSON request logging
- 10-model dropdown with 3 provider groups (OpenAI, Anthropic, Other)
- First test suite: 19 HTTP integration tests
[v0.2] UI Polish Pass
March 30, 2026
Visual audit via screenshot analysis. No new features -- design refinement only.
- Nav tabs: icon-only with CSS tooltip (5 tabs, no overflow)
- Session list: grouped by Today / Yesterday / Earlier
- Active session: blue left border accent
- Role labels: Title Case, softened color, circular icons
- Code blocks: connected language header with separator
- Send button: gradient + hover lift
- Composer: blue glow ring on focus
- Toast: frosted glass with float animation
- Tool status moved from composer footer to activity bar above composer
- Empty session flood fixed (filter + cleanup endpoint + test autouse)
[v0.1] Initial Build
March 30, 2026
Single-file web UI for Hermes. stdlib HTTP server, no external dependencies. Three-panel layout: sessions sidebar, chat area, workspace panel.
Core capabilities:
- Send messages, receive SSE-streamed responses
- Session create/load/delete, auto-title from first message
- File upload with manual multipart parser
- Workspace file tree with directory navigation
- Tool approval card (4 choices: once, session, always, deny)
- INFLIGHT session-switch guard
- 10-model dropdown (OpenAI, Anthropic, Other)
- SSH tunnel access on port 8787
Last updated: v0.34, April 5, 2026 | Tests: 433