- Project compressed roots to their continuation tip in session lists.
- Search title/content candidates through logical compression lineage.
- Hydrate detail views along the requested continuation branch while preserving requested ids.
- Scope model-context cache lookup by provider to avoid same-name cross-provider matches.
- Add regression coverage for lineage and provider lookup behavior.
* fix: group chat UI background colors and replace console.log in context-engine
- Set message list background to $bg-card to match single chat
- Set status-bar background to transparent
- Replace all console.log/warn with logger in context-engine compressor
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: group chat mobile UX improvements
- Add backdrop overlay for mobile sidebar with tap-to-close
- Auto-collapse sidebar on room select in mobile
- Move timestamp below message bubble
- Widen msg-body max-width to 85% to match single chat
- Add left padding to chat-header to avoid hamburger overlap
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: restore group chat system with Socket.IO and SQLite persistence
- GroupChatServer: Socket.IO server with room management, message history, typing indicators
- SQLite storage for rooms, messages, and agent configuration
- AgentClients: manages AI agent connections via socket.io-client, forwards @mentions to Hermes gateway
- REST API: room CRUD, agent management, invite codes
- Agent auto-restoration on server restart
- Tests for all REST endpoints
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add context-engine design document for group chat compression
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: handle special-character session search
* fix: keep unicode dotted session search on quoted FTS path
* feat: add context engine and group chat frontend UI
- Context engine: three-zone compression (head/tail/summary) with LLM
summarization, incremental updates, TTL cache, and graceful degradation
- Frontend: group chat page with Socket.IO client, room sidebar, message
list, agent/member display, create/join-by-code modals
- Integration: wire context engine into agent-clients before /v1/runs
- Refactor ChatStorage to use global DB (getDb/ensureTable) with gc_ prefix
- Add i18n keys for group chat to all 8 locales
- Add sidebar nav entry and router for group chat page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove leftover main branch code from merge conflict resolution
The `isNumericQuery`, `hasUnsafeChars`, and `runLikeContentSearch` functions
no longer exist — they were replaced by HEAD's `shouldUseLiteralContentSearch`
and `runLiteralContentSearch`. This dead code block caused a TypeScript
compile error after the merge.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: install missing socket.io dep and type ack params
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: enable WebSocket proxy and fix socket.io transport for group chat
- Add ws: true to Vite proxy config so WebSocket upgrade requests
are forwarded to the backend
- Allow both polling and websocket transports on server and client
(polling as fallback when WebSocket upgrade fails through proxy)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: separate socket.io path from REST routes for group chat
socket.io was mounted at /api/hermes/group-chat which intercepted all
REST requests to /api/hermes/group-chat/rooms etc, returning
"Transport unknown". Changed socket.io path to /api/hermes/group-chat/ws
to avoid conflicts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: improve group chat UI, agent management, and socket.io reliability
- Redesign GroupChatPanel with Naive UI, stacked agent avatars, and popover management
- Match GroupChatInput style with single chat input, add IME composition handling
- Add agent add/remove per room with profile selection and duplicate prevention
- Use @multiavatar for SVG avatar generation with caching
- Decouple joinRoom from socket.io, use REST API for data loading
- Switch socket.io to default path with /group-chat namespace to avoid proxy conflicts
- Restore agent connections after server is listening
- Add getRoomDetail REST endpoint and duplicate agent prevention (409)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: server-side @mention routing with context compression status and queue
- Move @mention detection from agent socket listeners to server-side processMentions()
- Add per-room processing lock to block mention dispatch during compression
- Queue mentions during processing, drain only the latest when ready
- Emit context_status events (compressing/replying/ready) to room via Socket.IO
- Frontend displays compression status indicator above input
- Token-based compression trigger (100k threshold) with CJK-aware estimation
- Fix compressor type errors (countTokens parameter type)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: improve group chat profile handling and session sync
Refine group chat room/session behavior with per-room compression controls, sidebar updates, and better stale session cleanup so multi-profile group chat state stays consistent.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: group chat improvements — session lifecycle, typing recovery, mention highlighting
- Fix cross-profile session deletion with deferred delete queue
- Move saveSessionProfile to after gateway response confirmation
- Replace all console.log with logger in group-chat modules
- Add server-side typing/context_status state tracking for room rejoin
- Fix @ mention popup position to follow cursor
- Add @ mention highlighting (blue) in chat message content
- Fix mention regex to match all occurrences after HTML tags
- Enable esbuild minify and treeShaking
- Move @multiavatar/multiavatar to devDependencies
- Add i18n keys for group chat features
- Update tests for new functionality
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: bump version to 0.4.5 and move @multiavatar to devDependencies
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Zhicheng Han <zhicheng.han@mathematik.uni-goettingen.de>
* fix: context-length API returns 200K instead of actual model context
Two bugs cause the /api/hermes/sessions/context-length endpoint to
always return DEFAULT_CONTEXT_LENGTH (200K):
1. getModelContextLength ignores config.yaml model.context_length
The function only checks models_dev_cache.json (which doesn't
exist in default installations) and falls back to the hardcoded
200K default, completely ignoring the user's explicit
model.context_length setting in config.yaml.
2. getDefaultModel regex fails when api_key/base_url come before default
The regex /^model:\s*\n\s+default:\s*(.+)$/m assumes 'default' is
the first child key under 'model:', but when api_key or base_url
appear first in the YAML, the match fails. This causes
getModelContextLength to short-circuit to DEFAULT_CONTEXT_LENGTH
before even reaching the cache lookup.
Fix:
- Add getDefaultModelRobust() that extracts the entire model: block
first, then searches for default: within it
- Add getConfigContextLength() that reads model.context_length from
config.yaml as a fallback (matching hermes-agent priority)
- Update getModelContextLength() resolution order:
1. models_dev_cache.json (existing)
2. config.yaml model.context_length (new)
3. DEFAULT_CONTEXT_LENGTH (existing fallback)
Closes#169
* refactor: rewrite model-context to use js-yaml, add context_length to provider form
- Replace fragile regex-based YAML parsing with js-yaml for reliable config.yaml reads
- Fix context_length resolution priority: config.yaml override > custom_providers > models_dev_cache > 200K default
- Add context_length input field when adding custom providers in ProviderFormModal
- Backend: persist context_length to custom_providers models.<model>.context_length in config.yaml
- Add i18n keys (contextLength, contextLengthPlaceholder) to all 8 locales
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use NInputNumber instead of NInput type=number for context_length
NInput does not support type="number" in Naive UI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: devilardis <53129661@qq.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Log rotation previously only ran at startup, causing logs to grow
indefinitely on long-running processes (reported up to 71GB/day).
Now checks file size every 60 seconds and truncates when exceeding 3MB.
Fixes#155
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Display persistent warning bar when Node.js version < 23
- Fix provider model fetching to support non-v1 API versions (e.g. /v4)
- Add v0.4.4 changelog entries to frontend
- Bump version to 0.4.4
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add file browser and file download with multi-backend support
Adds a built-in File Browser page and a File Download system to Hermes
Web UI, enabling users to browse, edit, preview, upload, and download
files from the workspace directly from the web dashboard.
File Browser (/hermes/files):
- New view FilesView.vue plus components under components/hermes/files/
(FileTree, FileList, FileBreadcrumb, FileToolbar, FileContextMenu,
FileEditor, FilePreview, FileRenameModal, FileUploadModal)
- New Pinia store stores/hermes/files.ts for directory tree, selection,
and editing state
- New API module api/hermes/files.ts
- New server routes routes/hermes/files.ts with CRUD, rename, upload,
and directory listing
- New service services/hermes/file-provider.ts with a pluggable
provider architecture (local filesystem + multi-terminal backends)
File Download:
- New server route routes/hermes/download.ts and client API
api/hermes/download.ts
- Integration in chat messages (MessageItem.vue, MarkdownRenderer.vue)
to surface downloadable file references
Packaging:
- package.json: add a prepare script so the package can be installed
directly from a git URL with dist/ built automatically
i18n: add files/download translations to en.ts and zh.ts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: use clipboard fallback for non-secure HTTP contexts
navigator.clipboard is undefined on HTTP intranet deployments (only
available in secure contexts). The previous synchronous calls threw
silently and the success toast still fired, making 'copy' actions
appear broken.
- Add packages/client/src/utils/clipboard.ts with execCommand fallback
via a hidden textarea
- Use the helper in FileContextMenu (copy file path), CodexLoginModal
(copy user code), NousLoginModal (copy user code), ChatPanel (copy
session id)
- Each call now awaits the result and shows success/failure based on
the actual outcome
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The isOAuthAuthorized check only looked for Codex's nested
`providers.{key}.tokens.access_token` structure, missing Nous's flat
`providers.nous.access_token`. Now checks both paths so all OAuth
providers are correctly detected and displayed in the provider list.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Add StepFun provider (API key auth, STEPFUN_API_KEY)
- Add Nous Portal provider with full OAuth device code flow
(device code request → poll for token → mint agent key → save to auth.json)
- Add NousLoginModal component for OAuth UI (user code display + verification link)
- Update ProviderFormModal to handle Nous OAuth flow (hide API key fields)
- Add nous-auth backend controller and routes
- Update PROVIDER_ENV_MAP with stepfun and nous entries
- Add i18n translations for Nous OAuth in all 8 locales
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Add username/password login as additional auth mechanism alongside existing token
- First login must use token; password can be configured in Settings > Account
- Password login returns the existing static token (no auth middleware changes)
- Add account settings: setup, change password, change username, remove password
- Add logout button to sidebar footer
- Add version changelog popup (click version number in sidebar)
- Support all 8 locales (en, zh, de, es, fr, ja, ko, pt)
- Bump version to 0.4.3
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: specify TS_NODE_PROJECT for dev:server script
ts-node/register resolves tsconfig from the entry file upward,
finding the root solution-style tsconfig.json (no compilerOptions).
This causes target to default to ES3, breaking MapIterator spread
syntax (TS2802). Set TS_NODE_PROJECT env var to point to the server
tsconfig which targets ES2024.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add token usage tracking, context display, and dynamic context length
- Intercept SSE proxy to capture run.completed events and persist token
usage (input_tokens, output_tokens) per session to SQLite/JSON store
- Display context usage bar in ChatInput showing used/total/remaining tokens
- Resolve actual context length from Hermes models_dev_cache.json based
on the active profile's default model (fallback 200K), with 5min in-memory cache
- Move sessions-db.ts to db/hermes/ for unified database layer
- Add usage store with SQLite + JSON fallback (auto-migration via ensureTable)
- Fix proxy SSE path regex to match rewritten upstream path
- Fix route ordering: /sessions/usage before /sessions/:id to avoid 404
- Fetch per-session usage on session enter instead of batch
- Add unit tests for usage-store, db index, and proxy SSE interception
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add single-page live session monitor and chat pinning
* fix: restore full test green after main merge
* fix: use Array.from instead of Set spread for ts-node compatibility
[...new Set()] requires downlevelIteration which isn't enabled in
ts-node dev mode, causing sonic-boom crash on startup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: ekko <fqsy1416@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Add glm-coding-plan to PROVIDER_ENV_MAP for proper env mapping
- Rename GLMCodingPlan value from 'glm' to 'glm-coding-plan' (kebab-case)
- Match custom providers against PROVIDER_PRESETS to reuse builtin models
- Fix provider key matching in create/update (use entry.name consistently)
- Clear stale base_url/api_key from config on provider create
- Clear model config when all providers are removed
- Add gateway restart on provider remove
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(chat): polish syntax highlighting and tool payload rendering (#94)
* [verified] feat(chat): polish syntax highlighting and tool payload rendering
* [verified] fix(chat): tighten large tool payload rendering
* docs: update data volume path in Docker docs
Align documentation with docker-compose.yml change:
hermes-web-ui-data -> hermes-web-ui, /app/dist/data -> /root/.hermes-web-ui
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: bundle server build and restructure service modules
- Add build-server.mjs script for standalone server compilation
- Add logger service with structured output
- Restructure auth, gateway-manager, hermes-cli, hermes services
- Update docker-compose volume mount path
- Update tsconfig and entry point for bundled server
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: separate controllers from routes and centralize route registration
- Extract business logic from route handlers into controllers/
- Add centralized route registry in routes/index.ts with public/auth/protected layers
- Replace global auth whitelist with sequential middleware registration
- Extract shared helpers to services/config-helpers.ts
- Allow custom provider name to be user-editable in ProviderFormModal
- Deduplicate custom providers by poolKey instead of base_url in getAvailable
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: auth bypass via path case, SPA serving, and provider improvements
- Fix auth bypass: path case-insensitive check for /api, /v1, /upload
- Fix SPA returning 401: skip auth for non-API paths (static files)
- Fix profile switch: use local loading state instead of shared store ref
- Auto-append /v1 to base_url when fetching models (frontend + backend)
- Guard .env writing to built-in providers only
- Add builtin field to provider presets, enable base_url input in form
- Print auth token to console on startup (pino only writes to file)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Zhicheng Han <43314240+hanzckernel@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: extract inline middleware from index.ts into separate modules
- Extract update middleware to routes/update.ts
- Extract health middleware and version logic to routes/health.ts
- Extract shutdown logic to services/shutdown.ts
- Extract gateway init to services/gateway-bootstrap.ts
- Remove unused variables, fix duplicate app creation
- Bump version to 0.4.0
index.ts: 260 lines → 80 lines
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: require auth for file upload and add 50MB size limit
Fixes#86
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Extract update middleware to routes/update.ts
- Extract health middleware and version logic to routes/health.ts
- Extract shutdown logic to services/shutdown.ts
- Extract gateway init to services/gateway-bootstrap.ts
- Remove unused variables, fix duplicate app creation
- Bump version to 0.4.0
index.ts: 260 lines → 80 lines
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Normalize request path to lowercase before auth check to prevent
bypassing authentication with uppercase paths like /API/hermes/sessions
- Auto-restart server after in-page update via detached hermes-web-ui restart
Closes#77
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Profile-aware proxy: inject API key from profile-specific .env, route requests via X-Hermes-Profile header
- Remove auth.json dependency: built-in providers use .env, custom providers use config.yaml
- Add allProviders field to available-models response with all hardcoded provider catalogs
- Add Models tab in Settings for editing provider API keys (built-in → .env, custom → config.yaml)
- Add PUT /api/config/providers/:poolKey for updating provider credentials
- ProviderFormModal uses backend allProviders for preset dropdown
- Gateway log format support: parse both agent and gateway log formats
- Add webui server.log to log viewer with log rotation at 3MB
- Fix provider delete loading state and OAuth provider cleanup
- Setup script: require Node.js 23+, auto-upgrade if version too low
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add GatewayManager for multi-profile gateway lifecycle management
- Auto-detect running gateways on startup via PID + health check
- Port conflict detection: check managed gateways, allocated ports, and
system-level port availability (TCP bind test)
- Two-phase startup: sequential port resolution, parallel process launch
- Use `gateway start/restart` on normal systems, `gateway run --replace`
on WSL/Docker
- Wait for health check before returning start/stop responses
- Add Gateways page with card-based layout showing profile status
- Reorganize sidebar navigation into collapsible groups
- Hide API server settings (now auto-managed by GatewayManager)
- Profile switch reloads page; Ctrl+C no longer stops gateways
- Remove redundant ensureApiServerConfig from index.ts and profiles.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace static top-level import with runtime version check and dynamic
import() so Node < 22.5 gracefully falls back to CLI path instead of
crashing at module load time.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SQLite path was returning null title for sessions without an explicit
title, while the CLI path derives it from the first user message.
Now uses the preview (first user message content) as title fallback,
matching the original CLI behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix#25: job update sends schedule as plain string but upstream expects
{ kind, expr, display } object, causing "'str' object has no attribute 'get'"
- Move hermes-cli.ts, hermes.ts, hermes-profile.ts into services/hermes/
for multi-agent namespacing consistency
- Fix ts-node Set spread compatibility in filesystem.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- add docker-compose setup with hermes-agent + hermes-webui
- make runtime config env-driven (compose vars + HERMES_BIN)
- improve gateway startup/restart resilience in docker
- make base image configurable via BASE_IMAGE/HERMES_AGENT_IMAGE
Closes https://github.com/EKKOLearnAI/hermes-web-ui/issues/14
- Add hermes-profile.ts for dynamic profile path resolution (all backend
routes now read from active profile directory instead of hardcoded ~/.hermes/)
- Add profile switcher dropdown in sidebar, reload page on switch
- Sync PROVIDER_PRESETS with Hermes CLI (fix keys: kimi-coding→kimi-for-coding,
kilocode→kilo, ai-gateway→vercel, opencode-zen→opencode; remove moonshot)
- Sync PROVIDER_ENV_MAP with Hermes models.dev + overlays (correct env var names)
- Add gateway restart after adding model provider
- Don't write GLM_BASE_URL/KIMI_BASE_URL for zai/kimi (let Hermes auto-detect)
- Write API keys to .env and credential_pool for all providers
- Built-in providers skip custom_providers in config.yaml
- Add debounce + per-field loading state for channel settings inputs
- Run hermes setup --reset for profiles without config.yaml
- Create empty .env for new profiles (not copied from default)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>