* refactor: migrate from /v1/runs to /v1/responses streaming API
Replace EventSource-based polling with direct SSE streaming via the
/v1/responses endpoint across all server-side callers (chat-run-socket,
context-compressor, gateway-client, agent-clients). Messages are now
written to DB in real-time during streaming, eliminating post-run sync.
Frontend chat store adds tool_call_id tracking for deduplication.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore: bump version to 0.5.16 and add changelog
- Persist real API usage to usage table on response.completed
- Remove unused codex_reasoning_items field from message schema
- Fix unused variable warnings in chat-run-socket
- Bump version to 0.5.16
- Add changelog entries for 0.5.16 (8 locales)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Add a website icon link (globe) alongside the GitHub icon in the
sidebar footer. Shorten version label from "Hermes Web UI" to "Web UI".
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
- Fix kanban loading spinner flickering on auto-refresh (silent mode)
- Fix group chat room-list transparent background on mobile
- Fix group chat sidebar auto-opening on mobile entry
- Fix page-header title overlapped by hamburger button on mobile
- Move hamburger button position to top: 10px
- Add changelog note about upgrading hermes-agent for kanban support
- Add i18n translations for all 8 locales
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix: support run approval prompts in chat
* fix(chat): render approval prompts
* fix(chat): dedupe approval pattern labels
* chore: sync approval flow with current main
- update Hermes Agent approval support guidance to PR #21899
- initialize Hermes table schemas in session-sync tests
* feat: add IP-based login brute-force protection
- Per-IP rate limiting: 3 failed login attempts locks the IP for 1 hour
- Separate counters for password login and token auth
- Global safety net: 20 req/min, hard lock after 50 total failures
- Persistent lock state to ~/.hermes-web-ui/.login-lock.json (survives restarts)
- Manual unlock: edit or delete the lock file
- Frontend handles 429/503 responses with localized error messages
- i18n support for 8 languages
* feat: add locked IP management endpoint and UI
- GET /api/auth/locked-ips: list all currently locked IPs (protected)
- DELETE /api/auth/locked-ips/:ip: unlock a specific IP (protected)
- DELETE /api/auth/locked-ips: unlock all IPs (protected)
- AccountSettings: shows locked IPs with remaining time, unlock buttons
- i18n support for 8 languages
- Clean up stale .js artifacts, add .gitignore rule
* fix: cross-type IP lock and IPv6-compatible unlock route
- Password and token login now share IP lock state: if an IP is locked
by either method, ALL auth methods are blocked for that IP
- Changed unlock endpoint from path param to query param (?ip=xxx) to
support IPv6 addresses containing colons
- Merged unlockIp and unlockAll into a single handler
* chore: increase global login rate limit from 20 to 100 requests per minute
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: ekko <fqsy1416@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
FUN-Codex: add GPT models (5.5, 5.4, 5.4-mini, 5.3-codex, 5.3-codex-spark)
FUN-Claude: replace with actual Claude models from API (opus-4-7 down to 3-5-haiku)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
TerminalPanel was connecting on mount even when the drawer was closed
and the terminal tab was inactive. Combined with reconnectAttempts
resetting on every ws.onopen, this caused infinite reconnection loops
that spawned PTY processes until system limits were hit.
- Pass `visible` prop to TerminalPanel, only connect when terminal tab
is actually shown
- Move reconnectAttempts reset from ws.onopen to "created" handler so
only successful PTY creation resets the counter
- Remove unused onMounted import
Fixes#509
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
PR #470 changed the default listen host to undefined, letting Node.js
bind to IPv6 :: on systems that support it. This broke WSL2 where IPv6
dual-stack is unreliable — the server binds to :: but IPv4 127.0.0.1
connections fail, causing the health check to time out.
Revert to 0.0.0.0 as the default. Users who need IPv6 can set
BIND_HOST=:: explicitly.
Fixes#518
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Add export functionality that allows users to download session data
as JSON or plain text, with optional LLM-based context compression
for long conversations. Includes UI controls in chat panel, session
list, and history view, plus i18n strings for all 8 locales.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Allow sending multiple messages while a run is active. Messages are
queued on the server and processed sequentially after each run
completes. Each completed assistant message triggers speech playback
independently, and the UI shows queue status with a badge indicator.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
The @ symbol is a special character in vue-i18n (used for linked formats).
Removing it from all changelog entries fixes:
- Chinese: @mention → mention
- English: @mention → mention
- All other languages: @mention → mention
This resolves the SyntaxError in message compilation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bug: In group chat input, using keyboard (ArrowDown/ArrowUp + Enter) to
select an agent from the @mention dropdown always inserts the wrong agent
name (the first one), regardless of which item is visually highlighted.
Mouse click works correctly.
Root cause: naive-ui's NDropdown has its own internal keyboard state machine
that is independent from the component's activeIndex ref. When Enter is
pressed, NDropdown fires @select using its own stale index before
handleKeydown runs, always selecting the wrong agent. NDropdown exposes no
public API to synchronize its internal state, making this unfixable in place.
Fix: Replace NDropdown with a custom <div class="mention-dropdown">
rendered via v-for, with fully manual keyboard/click/hover control. This
eliminates the dual-state conflict entirely — there's a single activeIndex
for all interactions.
Additional improvements over the previous NDropdown-based implementation:
- Scroll follows the active item automatically (scrollToActive)
- Dropdown flips upward when insufficient space below (smart placement)
- Click-outside-to-close via document-level listener
- Transition animation matching NDropdown's fade-in-scale-up exactly
(0.2s cubic-bezier, scale 0.9->1 with opacity fade)
Co-authored-by: Fix Contributor <fix-contributor@hermes-web-ui.dev>
* feat: add batch delete functionality for chat sessions
Backend:
- Add batchRemove controller to handle bulk session deletion
- Add POST /api/hermes/sessions/batch-delete endpoint
- Support both local session store and CLI deletion
- Return detailed results (deleted, failed, errors)
Frontend:
- Add batch selection mode with checkboxes in SessionListItem
- Add batch selection toggle and select all button
- Add batch delete button with confirmation
- Update ChatPanel to manage selected session IDs
- Add batchDeleteSessions API function
i18n:
- Add batch delete translations for all 8 languages
- Simplify "Web UI/API Server Sessions" to "Sessions"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: vertically align buttons in session list header
Add inline-flex and center alignment to all buttons in session-list-actions
to ensure proper vertical centering with the title text.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: ensure proper vertical alignment in session list header
- Set fixed height of 22px for session-list-actions
- Add min-height and height to all buttons
- Add line-height to session-list-title for text baseline alignment
- Add min-height: 0 to session-list-header to prevent flex stretch
This ensures the title and all action buttons are perfectly vertically centered.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: call loadSessions after batch delete instead of looping deleteSession
The previous implementation was calling chatStore.deleteSession(id) in a loop
after batch delete API succeeded, which triggered individual delete API calls
for each session - causing n API requests instead of 1.
Now we simply call loadSessions() to refresh the session list from the server
after successful batch deletion, ensuring:
- Only 1 API request for batch delete
- UI stays in sync with server state
- No duplicate API calls
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: improve update mechanism reliability
Major improvements to the update system:
**Path Resolution:**
- Remove unreliable dirname(process.execPath) assumption
- Use npm from PATH environment variable
- Dynamically get global prefix via `npm prefix -g`
- Calculate CLI path based on actual global install location
**Windows Support:**
- Remove complex cmd.exe wrapper logic
- Directly call npm.cmd (works on all Windows setups)
- Simplified quote handling
**Error Handling:**
- Add fallback error message (err.stderr || err.message || String(err))
- Add default success message when output is empty
- Wrap spawnRestart in try-finally to ensure cleanup
**Timing:**
- Increase timeout from 120s to 10min (slow network support)
- Increase restart delay from 2s to 3s (safer margin)
**Code Quality:**
- Remove unused functions (getNodeBinDir, getWindowsShell, quoteForWindowsCommand)
- Use constants instead of magic numbers (10 * 60 * 1000)
- More maintainable and cross-platform compatible
This fixes issues where updates would fail due to incorrect npm/CLI paths
on systems with non-standard Node.js installations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add database table for model context length configuration
- Add model_context table with provider/model/context_limit fields
- Implement UPSERT endpoint for model context configuration
- Add priority lookup: database > config.yaml > custom_providers > cache
- Add frontend click-to-edit UI in ChatInput with tooltip
- Add i18n support for context editing dialog (all 8 locales)
- Use context_limit field consistently across frontend and backend
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: use useMessage() composable instead of window.$message in ChatInput
- Remove incorrect NMessage import (not a component)
- Use useMessage() composable from naive-ui
- Replace window.$message?.xxx() with message.xxx()
- Fixes TypeScript build errors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Frontend:
- Filter out assistant messages with empty content in mapHermesMessages()
- Prevents displaying 194/585 empty messages in long sessions
- Reduces visual clutter and improves UX
Backend:
- Add message count limit (<= 200) to compression threshold checks
- Prevents compressing sessions with too many messages
- Improves compression performance and reliability
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove streaming-indicator and tool calls panel from HistoryMessageList component.
History sessions should not show "typing" indicators as they are read-only, completed sessions.
Changes:
- Remove streaming-indicator video animation from history
- Remove tool calls panel from history
- Clean up unused imports and functions
- Remove unnecessary watchers
- Clean up related styles (~200 lines removed)
- Update version to 0.5.11 in package.json
- Update changelog version to 0.5.11
This fixes the issue where history sessions incorrectly showed streaming indicators
when there was an active run in another session.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: call upstream stop API when aborting a run
- Modified handleAbort to call POST /v1/runs/{run_id}/stop endpoint
- Use profile-specific upstream URL and API key from gatewayManager
- Add 5-second timeout with error handling and logging
- Keep local abortController.abort() for EventSource cleanup
- Change handleAbort to async method and update call site
This ensures the upstream Hermes gateway is properly notified
when a user aborts a run, allowing graceful termination.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: close ChatRunSocket connections on shutdown to prevent hanging
- Add close() method to ChatRunSocket to abort all active runs
and clear session state
- Pass chatRunServer to bindShutdown and close it before
groupChatServer during shutdown
- This prevents EventSource connections and abort controllers
from keeping the process alive during nodemon restart
Fixes the "still waiting for sub-process to finish" issue.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Handle chat run abort lifecycle
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Bump version from 0.5.9 to 0.5.10
- Add changelog entry for 0.5.10 release
- Update i18n translations for new changelog items
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Add approvals.mode configuration to allow users to enable/disable
session operation authorization. Mode can be 'off' (no auth) or 'manual'
(require auth). Changes trigger automatic gateway restart for config
to take effect.
- Add ApprovalConfig type with mode: 'off' | 'manual'
- Add approvals section to settings store
- Add session authorization toggle in SessionSettings UI
- Add approvals to PLATFORM_SECTIONS for auto-restart
- Add i18n support for all 8 languages
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: add missing i18n key and unify session data source (#408)
- Add `chat.sessionNotFound` translation key to all 8 locales
- Fix history page data source inconsistency:
- Change `getHermesSession` to prioritize database over CLI
- Now consistent with `listHermesSessions` behavior
- Prevents "session in list but detail not found" issue
- Update CI workflow to trigger on base branch PRs
- Remove debug log from sessions-db
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: filter special characters and emoji in speech playback (#409)
- Update extractReadableText to filter special characters like *#
- Only keep common punctuation marks for speech synthesis
- Remove emoji, symbols, and special unicode characters
- Improve text-to-speech readability
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add drawer panel with mobile sidebar support and customizable button (#412)
* feat: add drawer panel with mobile sidebar support
- Add DrawerPanel component with Terminal and Files tabs
- Extract TerminalPanel and FilesPanel from existing views
- Add mobile sidebar toggle functionality with overlay
- Add rainbow breathing light effect to drawer button
- Remove Tools section from AppSidebar (Terminal/Files entries)
- Add i18n support for drawer and file tree
- Optimize mobile button layout and spacing
- Fix z-index hierarchy for proper layering
- Add responsive sidebar behavior (PC: always visible, Mobile: toggle)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: customize drawer button with arc rainbow border
- Change drawer button to semi-circle shape贴着右边
- Add arrow icon pointing left (向左箭头)
- Add rainbow border from top to bottom through semi-circle arc
- Slow down animation from 4s to 8s for smoother effect
- Move drawer button wrapper to messages area only (not贯穿header和input)
- Add semi-transparent accent color background to button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: resolve profile switching state sync issue (#414) (#415)
* fix: resolve profile switching state sync issue (#414)
Fix bug where switching to a different profile would still show the
old profile name in the UI and prevent switching back to default.
Root cause:
- Frontend relied entirely on fetchProfiles() return value to set
activeProfileName
- Backend Hermes CLI may return stale active flag due to timing
issues between profile use and profile list commands
- This caused frontend to display wrong profile and prevented
switching back to default
Solution:
- Immediately set activeProfileName when switchProfile API succeeds
- Don't rely solely on listProfiles() result which may have stale data
- Use activeProfileName instead of activeProfile?.name in ProfileSelector
Changes:
- profiles store: Set activeProfileName immediately after successful switch
- ProfileSelector: Use activeProfileName computed property
- Add test to verify activeProfileName updates on switch
Fixes#414
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refine: improve error handling for profile switching failures
Add proper error handling for edge cases:
- If fetchProfiles() fails after successful switch, keep the updated
activeProfileName (don't let fetchProfiles failure undo the switch)
- Add test cases to verify:
1. API failure doesn't change state
2. fetchProfiles failure doesn't affect successful switch
This ensures the UI remains consistent even when profile list refresh
fails after a successful profile switch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refine: add rollback mechanism for profile switching verification
Add backend verification after profile switch:
- Save old activeProfileName before setting new value
- After fetchProfiles, verify backend reports expected active profile
- If backend reports different profile, rollback frontend state and return false
- This handles edge case where API returns 200 but backend didn't actually switch
Test cases:
- ✅ Normal switch: updates and verifies successfully
- ✅ API failure: doesn't change state
- ✅ fetchProfiles failure: assumes success (API returned 200)
- ✅ Backend verification fails: rolls back to old profile
This ensures frontend state always matches backend reality, even in
edge cases where hermes profile use succeeded but gateway/cleanup
steps failed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refine: add user feedback for profile operations
Improve user experience with success/error messages:
- ProfileSelector: Add error message when switch fails
- ProfileCard: Add success message before reload on switch
- ProfileSelector: Use async/await for better error handling
- ProfileCard: Add 500ms delay before reload to show success message
Before: Silent failures, no feedback
After: Clear success/error messages for all operations
Example feedback:
- Success: "已切换到配置 qinghe"
- Failure: "切换配置失败,网关可能需要手动重启"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: update frontend changelog for v0.5.7 (#419)
* docs: update frontend changelog for v0.5.7
- Update changelog.ts with v0.5.7 release date and changes
- Add i18n translation keys for all languages (en, zh, de, es, fr, ja, ko, pt)
- Include v0.5.7 changelog entries:
- Optimize context compression and session sync
- Add startup delays to prevent database race conditions
Changes:
- packages/client/src/data/changelog.ts: Update v0.5.7 entry
- packages/client/src/i18n/locales/*.ts: Add changelog translation section
This enables the changelog modal in the UI to display v0.5.7 release notes.
* feat: add v0.5.7 changelog translations to all supported languages
Add new_0_5_7_1, new_0_5_7_2, and new_0_5_7_3 changelog entries to all
locale files (en, zh, de, es, fr, ja, ko, pt) with proper translations
for each language.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: remove duplicate changelog sections causing syntax errors
Remove duplicate changelog object sections that were causing TypeScript
syntax errors in all locale files (en, zh, de, es, fr, ja, ko, pt).
The actual changelog entries are already correctly placed in the main
changelog section of each file.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add v0.5.8 changelog and fix profile parsing issue
Add v0.5.8 changelog entries based on PRs merged since v0.5.7:
- Drawer panel with mobile sidebar support (#412)
- Profile switching state sync fix (#414)
- Speech playback special character filtering (#409)
- Missing i18n key and session data source unification (#408)
- Vite build optimization for faster Docker builds (#403)
Also fix issue #417: Profile names with long hyphenated names fail
to parse in profile list regex. Change \s{2,} to \s+ to handle
compressed column spacing when profile names are long.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: remove enter key submit from profile creation and rename modals
Remove @keyup.enter handlers from NInput components in:
- ProfileCreateModal: prevent accidental profile creation when pressing enter
- ProfileRenameModal: prevent accidental profile rename when pressing enter
Users must now explicitly click the confirm button to submit, preventing
unintended profile operations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: allow free text input for profile names
Remove frontend character filtering from profile creation and rename
modals. Users can now input any characters including spaces and
uppercase letters to test backend Hermes CLI validation.
Changes:
- ProfileCreateModal: Remove toLowerCase() and character filtering
- ProfileRenameModal: Remove toLowerCase() and character filtering
- Use v-model:value binding instead of :value with @input
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: improve error handling for profile creation
Display backend error messages when profile creation fails instead of
generic "failed" message. This helps users understand why their
profile name was rejected (e.g., invalid characters).
Changes:
- API layer: Capture and return error messages from backend
- ProfileCreateModal: Display specific error message from backend
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add profile name validation with i18n support
Add client-side validation for profile names to prevent invalid input
before sending to backend. Only lowercase letters, numbers, underscores,
and hyphens are allowed.
Changes:
- ProfileCreateModal: Add input validation with real-time feedback
- ProfileRenameModal: Add input validation with real-time feedback
- Add nameValidation i18n key for all 8 languages
- Filter invalid characters on input and show warning message
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: revert profile parsing regex changes
Revert the regex changes in hermes-cli.ts and gateway-manager.ts
back to requiring \s{2,} (at least 2 spaces). Since frontend now
validates profile names to only allow lowercase letters, numbers,
underscores, and hyphens, the relaxed regex is no longer needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: revert profile parsing regex changes
Revert the regex changes in gateway-manager.ts and hermes-cli.ts
back to requiring \s{2,} (at least 2 spaces). Since frontend now
validates profile names to only allow lowercase letters, numbers,
underscores, and hyphens, the relaxed regex is no longer needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: remove tooltip from drawer button
Remove the NTooltip wrapper from the floating drawer button.
The "Terminal & Files" tooltip is no longer shown on hover.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update assets images (#421)
Updated two asset images in the client package.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: bump version to 0.5.8
Release v0.5.8
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: improve profile list parsing to handle long profile names (#425)
Fixed issue #423 where long profile names caused parsing failures.
Changes:
- gateway-manager.ts: Use `.+?` instead of `\S+` to match profile names, allowing names that overflow table column width
- hermes-cli.ts: Use `\s+` instead of `\s{2,}` for first delimiter to handle cases where long profile names reduce spacing to 1 space
The regex now correctly parses profile output even when profile names are long enough to compress table formatting, ensuring all profiles appear in the UI regardless of name length.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add GitHub issue templates
Add structured issue templates to guide users when submitting issues:
- Bug Report template with version info, reproduction steps, and environment details
- Feature Request template with problem statement, solution, and priority
- General Issue template for questions that don't fit other categories
- Config to enable blank issues and provide contact links to documentation and discussions
Templates use YAML forms for better structure and validation of required fields.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: unify profile management across the application (#432)
This commit addresses long-standing profile inconsistency issues by establishing
`~/.hermes/active_profile` file as the single source of truth for all profile
operations throughout the application.
## Changes
### Backend (Server)
**1. profiles.ts - Enhanced profile switching**
- Switch from CLI polling to direct file verification (Hermes CLI writes synchronously)
- Verify `active_profile` file with quick retry (max 2 attempts × 100ms = 300ms)
- Update GatewayManager only after file verification succeeds
- Add comprehensive logging for debugging
**2. profiles.ts - Authoritative API responses**
- Override CLI's active flag with `active_profile` file in `list()` endpoint
- Add warning when CLI output differs from file (detects inconsistencies)
- Ensures API responses always match actual runtime state
**3. jobs.ts - Use authoritative profile source**
- `resolveProfile()` falls back to `getActiveProfileName()` when no profile in request
- Ensures jobs operate on correct profile even if frontend doesn't specify
**4. cron-history.ts - Fix run history to respect active profile**
- Changed from fixed `~/.hermes/cron/output/` to `getActiveProfileDir()/cron/output/`
- Run history now correctly switches with profile (e.g., `~/.hermes/profiles/hermes/cron/output/`)
**5. proxy-handler.ts - Add fallback to authoritative source**
- If no profile in request headers/query, read from `getActiveProfileName()`
- Prevents proxy from using wrong default profile
### Frontend (Client)
**1. api/client.ts - Simplified profile resolution**
- Prioritize `useProfilesStore().activeProfileName` over localStorage
- localStorage fallback only for early initialization
**2. api/hermes/chat.ts - Consistent profile resolution**
- Same pattern: store first, localStorage fallback only during init
**3. stores/session-browser-prefs.ts - Clean up fallback logic**
- Prioritize store, remove redundant localStorage read
## Problem Solved
Previously, multiple components had different ways of determining the active profile:
- CLI output (◆ marker) - could be stale
- GatewayManager memory - startup cache only
- localStorage - frontend cache
- Various fallbacks scattered across codebase
This caused inconsistencies where:
- Frontend showed one profile but API used another
- Jobs ran on wrong profile
- Run history displayed wrong data
- Profile switches appeared to fail (but actually succeeded)
## Solution
All components now derive the active profile from the same authoritative source:
- `~/.hermes/active_profile` file (written synchronously by `hermes profile use`)
- `getActiveProfileName()` function (reads the file)
- Single source of truth = no inconsistencies
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: add v0.5.9 changelog entries (#434)
- Add unified profile management across the application
- Add GitHub issue and pull request templates
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: Enhance Markdown Media Rendering (Image/Video/File Support) (#438)
* feat: enhance markdown media rendering with image, video, and file support
- Add image display with thumbnail preview (200x160px) and click-to-fullscreen
- Add video playback support for .mp4 and .webm formats with HTML5 player
- Add file card UI for downloads with icon and filename
- Convert local file paths (/tmp/*) to download URLs with auth token
- Add AI output format guidelines system prompt (llm-prompt.ts)
- Increase max download file size from 100MB to 200MB
- Add documentation for AI output format constraints
This enables AI agents to return images, videos, and files using standard
Markdown syntax, which the frontend renders as interactive media elements
instead of plain text links.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: resolve unused parameter TypeScript errors in MarkdownRenderer
Use underscore prefix for unused match parameters in replace callbacks
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: optimize group chat prompts and fix media handling (#439)
Group Chat Prompt Improvements:
- Add AI_OUTPUT_FORMAT_GUIDELINES to group chat system prompts
- Fix duplicate member issue in room member list (deduplicate by name)
- Handle empty agentDescription with default fallback
- Add rule for sending files to users using proper format
Chat Run Socket Integration:
- Integrate getSystemPrompt() into chat-run-socket.ts
- Append media format guidelines to all chat instructions
- Ensure consistent format enforcement across chat and group chat
Media Format Guidelines:
- Simplify "注意事项" section (remove frontend implementation details)
- Add "发送文件给用户" section with clear examples
- Update video format description to mention embedded player
URL Encoding Fix:
- Fix double URL encoding in download.ts (decode first, then encode)
- Prevent %25E8... double-encoded paths, now correctly %E8...
This ensures AI agents in both private chat and group chat follow
consistent media formatting rules when returning images, videos, and files.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: add missing i18n key and unify session data source (#408)
- Add `chat.sessionNotFound` translation key to all 8 locales
- Fix history page data source inconsistency:
- Change `getHermesSession` to prioritize database over CLI
- Now consistent with `listHermesSessions` behavior
- Prevents "session in list but detail not found" issue
- Update CI workflow to trigger on base branch PRs
- Remove debug log from sessions-db
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: filter special characters and emoji in speech playback (#409)
- Update extractReadableText to filter special characters like *#
- Only keep common punctuation marks for speech synthesis
- Remove emoji, symbols, and special unicode characters
- Improve text-to-speech readability
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add drawer panel with mobile sidebar support and customizable button (#412)
* feat: add drawer panel with mobile sidebar support
- Add DrawerPanel component with Terminal and Files tabs
- Extract TerminalPanel and FilesPanel from existing views
- Add mobile sidebar toggle functionality with overlay
- Add rainbow breathing light effect to drawer button
- Remove Tools section from AppSidebar (Terminal/Files entries)
- Add i18n support for drawer and file tree
- Optimize mobile button layout and spacing
- Fix z-index hierarchy for proper layering
- Add responsive sidebar behavior (PC: always visible, Mobile: toggle)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: customize drawer button with arc rainbow border
- Change drawer button to semi-circle shape贴着右边
- Add arrow icon pointing left (向左箭头)
- Add rainbow border from top to bottom through semi-circle arc
- Slow down animation from 4s to 8s for smoother effect
- Move drawer button wrapper to messages area only (not贯穿header和input)
- Add semi-transparent accent color background to button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: resolve profile switching state sync issue (#414) (#415)
* fix: resolve profile switching state sync issue (#414)
Fix bug where switching to a different profile would still show the
old profile name in the UI and prevent switching back to default.
Root cause:
- Frontend relied entirely on fetchProfiles() return value to set
activeProfileName
- Backend Hermes CLI may return stale active flag due to timing
issues between profile use and profile list commands
- This caused frontend to display wrong profile and prevented
switching back to default
Solution:
- Immediately set activeProfileName when switchProfile API succeeds
- Don't rely solely on listProfiles() result which may have stale data
- Use activeProfileName instead of activeProfile?.name in ProfileSelector
Changes:
- profiles store: Set activeProfileName immediately after successful switch
- ProfileSelector: Use activeProfileName computed property
- Add test to verify activeProfileName updates on switch
Fixes#414
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refine: improve error handling for profile switching failures
Add proper error handling for edge cases:
- If fetchProfiles() fails after successful switch, keep the updated
activeProfileName (don't let fetchProfiles failure undo the switch)
- Add test cases to verify:
1. API failure doesn't change state
2. fetchProfiles failure doesn't affect successful switch
This ensures the UI remains consistent even when profile list refresh
fails after a successful profile switch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refine: add rollback mechanism for profile switching verification
Add backend verification after profile switch:
- Save old activeProfileName before setting new value
- After fetchProfiles, verify backend reports expected active profile
- If backend reports different profile, rollback frontend state and return false
- This handles edge case where API returns 200 but backend didn't actually switch
Test cases:
- ✅ Normal switch: updates and verifies successfully
- ✅ API failure: doesn't change state
- ✅ fetchProfiles failure: assumes success (API returned 200)
- ✅ Backend verification fails: rolls back to old profile
This ensures frontend state always matches backend reality, even in
edge cases where hermes profile use succeeded but gateway/cleanup
steps failed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refine: add user feedback for profile operations
Improve user experience with success/error messages:
- ProfileSelector: Add error message when switch fails
- ProfileCard: Add success message before reload on switch
- ProfileSelector: Use async/await for better error handling
- ProfileCard: Add 500ms delay before reload to show success message
Before: Silent failures, no feedback
After: Clear success/error messages for all operations
Example feedback:
- Success: "已切换到配置 qinghe"
- Failure: "切换配置失败,网关可能需要手动重启"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: update frontend changelog for v0.5.7 (#419)
* docs: update frontend changelog for v0.5.7
- Update changelog.ts with v0.5.7 release date and changes
- Add i18n translation keys for all languages (en, zh, de, es, fr, ja, ko, pt)
- Include v0.5.7 changelog entries:
- Optimize context compression and session sync
- Add startup delays to prevent database race conditions
Changes:
- packages/client/src/data/changelog.ts: Update v0.5.7 entry
- packages/client/src/i18n/locales/*.ts: Add changelog translation section
This enables the changelog modal in the UI to display v0.5.7 release notes.
* feat: add v0.5.7 changelog translations to all supported languages
Add new_0_5_7_1, new_0_5_7_2, and new_0_5_7_3 changelog entries to all
locale files (en, zh, de, es, fr, ja, ko, pt) with proper translations
for each language.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: remove duplicate changelog sections causing syntax errors
Remove duplicate changelog object sections that were causing TypeScript
syntax errors in all locale files (en, zh, de, es, fr, ja, ko, pt).
The actual changelog entries are already correctly placed in the main
changelog section of each file.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add v0.5.8 changelog and fix profile parsing issue
Add v0.5.8 changelog entries based on PRs merged since v0.5.7:
- Drawer panel with mobile sidebar support (#412)
- Profile switching state sync fix (#414)
- Speech playback special character filtering (#409)
- Missing i18n key and session data source unification (#408)
- Vite build optimization for faster Docker builds (#403)
Also fix issue #417: Profile names with long hyphenated names fail
to parse in profile list regex. Change \s{2,} to \s+ to handle
compressed column spacing when profile names are long.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: remove enter key submit from profile creation and rename modals
Remove @keyup.enter handlers from NInput components in:
- ProfileCreateModal: prevent accidental profile creation when pressing enter
- ProfileRenameModal: prevent accidental profile rename when pressing enter
Users must now explicitly click the confirm button to submit, preventing
unintended profile operations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: allow free text input for profile names
Remove frontend character filtering from profile creation and rename
modals. Users can now input any characters including spaces and
uppercase letters to test backend Hermes CLI validation.
Changes:
- ProfileCreateModal: Remove toLowerCase() and character filtering
- ProfileRenameModal: Remove toLowerCase() and character filtering
- Use v-model:value binding instead of :value with @input
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: improve error handling for profile creation
Display backend error messages when profile creation fails instead of
generic "failed" message. This helps users understand why their
profile name was rejected (e.g., invalid characters).
Changes:
- API layer: Capture and return error messages from backend
- ProfileCreateModal: Display specific error message from backend
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add profile name validation with i18n support
Add client-side validation for profile names to prevent invalid input
before sending to backend. Only lowercase letters, numbers, underscores,
and hyphens are allowed.
Changes:
- ProfileCreateModal: Add input validation with real-time feedback
- ProfileRenameModal: Add input validation with real-time feedback
- Add nameValidation i18n key for all 8 languages
- Filter invalid characters on input and show warning message
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: revert profile parsing regex changes
Revert the regex changes in hermes-cli.ts and gateway-manager.ts
back to requiring \s{2,} (at least 2 spaces). Since frontend now
validates profile names to only allow lowercase letters, numbers,
underscores, and hyphens, the relaxed regex is no longer needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: revert profile parsing regex changes
Revert the regex changes in gateway-manager.ts and hermes-cli.ts
back to requiring \s{2,} (at least 2 spaces). Since frontend now
validates profile names to only allow lowercase letters, numbers,
underscores, and hyphens, the relaxed regex is no longer needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: remove tooltip from drawer button
Remove the NTooltip wrapper from the floating drawer button.
The "Terminal & Files" tooltip is no longer shown on hover.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update assets images (#421)
Updated two asset images in the client package.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: bump version to 0.5.8
Release v0.5.8
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: improve profile list parsing to handle long profile names (#425)
Fixed issue #423 where long profile names caused parsing failures.
Changes:
- gateway-manager.ts: Use `.+?` instead of `\S+` to match profile names, allowing names that overflow table column width
- hermes-cli.ts: Use `\s+` instead of `\s{2,}` for first delimiter to handle cases where long profile names reduce spacing to 1 space
The regex now correctly parses profile output even when profile names are long enough to compress table formatting, ensuring all profiles appear in the UI regardless of name length.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add GitHub issue templates
Add structured issue templates to guide users when submitting issues:
- Bug Report template with version info, reproduction steps, and environment details
- Feature Request template with problem statement, solution, and priority
- General Issue template for questions that don't fit other categories
- Config to enable blank issues and provide contact links to documentation and discussions
Templates use YAML forms for better structure and validation of required fields.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: unify profile management across the application (#432)
This commit addresses long-standing profile inconsistency issues by establishing
`~/.hermes/active_profile` file as the single source of truth for all profile
operations throughout the application.
## Changes
### Backend (Server)
**1. profiles.ts - Enhanced profile switching**
- Switch from CLI polling to direct file verification (Hermes CLI writes synchronously)
- Verify `active_profile` file with quick retry (max 2 attempts × 100ms = 300ms)
- Update GatewayManager only after file verification succeeds
- Add comprehensive logging for debugging
**2. profiles.ts - Authoritative API responses**
- Override CLI's active flag with `active_profile` file in `list()` endpoint
- Add warning when CLI output differs from file (detects inconsistencies)
- Ensures API responses always match actual runtime state
**3. jobs.ts - Use authoritative profile source**
- `resolveProfile()` falls back to `getActiveProfileName()` when no profile in request
- Ensures jobs operate on correct profile even if frontend doesn't specify
**4. cron-history.ts - Fix run history to respect active profile**
- Changed from fixed `~/.hermes/cron/output/` to `getActiveProfileDir()/cron/output/`
- Run history now correctly switches with profile (e.g., `~/.hermes/profiles/hermes/cron/output/`)
**5. proxy-handler.ts - Add fallback to authoritative source**
- If no profile in request headers/query, read from `getActiveProfileName()`
- Prevents proxy from using wrong default profile
### Frontend (Client)
**1. api/client.ts - Simplified profile resolution**
- Prioritize `useProfilesStore().activeProfileName` over localStorage
- localStorage fallback only for early initialization
**2. api/hermes/chat.ts - Consistent profile resolution**
- Same pattern: store first, localStorage fallback only during init
**3. stores/session-browser-prefs.ts - Clean up fallback logic**
- Prioritize store, remove redundant localStorage read
## Problem Solved
Previously, multiple components had different ways of determining the active profile:
- CLI output (◆ marker) - could be stale
- GatewayManager memory - startup cache only
- localStorage - frontend cache
- Various fallbacks scattered across codebase
This caused inconsistencies where:
- Frontend showed one profile but API used another
- Jobs ran on wrong profile
- Run history displayed wrong data
- Profile switches appeared to fail (but actually succeeded)
## Solution
All components now derive the active profile from the same authoritative source:
- `~/.hermes/active_profile` file (written synchronously by `hermes profile use`)
- `getActiveProfileName()` function (reads the file)
- Single source of truth = no inconsistencies
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: add v0.5.9 changelog entries (#434)
- Add unified profile management across the application
- Add GitHub issue and pull request templates
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: add missing i18n key and unify session data source (#408)
- Add `chat.sessionNotFound` translation key to all 8 locales
- Fix history page data source inconsistency:
- Change `getHermesSession` to prioritize database over CLI
- Now consistent with `listHermesSessions` behavior
- Prevents "session in list but detail not found" issue
- Update CI workflow to trigger on base branch PRs
- Remove debug log from sessions-db
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: filter special characters and emoji in speech playback (#409)
- Update extractReadableText to filter special characters like *#
- Only keep common punctuation marks for speech synthesis
- Remove emoji, symbols, and special unicode characters
- Improve text-to-speech readability
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add drawer panel with mobile sidebar support and customizable button (#412)
* feat: add drawer panel with mobile sidebar support
- Add DrawerPanel component with Terminal and Files tabs
- Extract TerminalPanel and FilesPanel from existing views
- Add mobile sidebar toggle functionality with overlay
- Add rainbow breathing light effect to drawer button
- Remove Tools section from AppSidebar (Terminal/Files entries)
- Add i18n support for drawer and file tree
- Optimize mobile button layout and spacing
- Fix z-index hierarchy for proper layering
- Add responsive sidebar behavior (PC: always visible, Mobile: toggle)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: customize drawer button with arc rainbow border
- Change drawer button to semi-circle shape贴着右边
- Add arrow icon pointing left (向左箭头)
- Add rainbow border from top to bottom through semi-circle arc
- Slow down animation from 4s to 8s for smoother effect
- Move drawer button wrapper to messages area only (not贯穿header和input)
- Add semi-transparent accent color background to button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: resolve profile switching state sync issue (#414) (#415)
* fix: resolve profile switching state sync issue (#414)
Fix bug where switching to a different profile would still show the
old profile name in the UI and prevent switching back to default.
Root cause:
- Frontend relied entirely on fetchProfiles() return value to set
activeProfileName
- Backend Hermes CLI may return stale active flag due to timing
issues between profile use and profile list commands
- This caused frontend to display wrong profile and prevented
switching back to default
Solution:
- Immediately set activeProfileName when switchProfile API succeeds
- Don't rely solely on listProfiles() result which may have stale data
- Use activeProfileName instead of activeProfile?.name in ProfileSelector
Changes:
- profiles store: Set activeProfileName immediately after successful switch
- ProfileSelector: Use activeProfileName computed property
- Add test to verify activeProfileName updates on switch
Fixes#414
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refine: improve error handling for profile switching failures
Add proper error handling for edge cases:
- If fetchProfiles() fails after successful switch, keep the updated
activeProfileName (don't let fetchProfiles failure undo the switch)
- Add test cases to verify:
1. API failure doesn't change state
2. fetchProfiles failure doesn't affect successful switch
This ensures the UI remains consistent even when profile list refresh
fails after a successful profile switch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refine: add rollback mechanism for profile switching verification
Add backend verification after profile switch:
- Save old activeProfileName before setting new value
- After fetchProfiles, verify backend reports expected active profile
- If backend reports different profile, rollback frontend state and return false
- This handles edge case where API returns 200 but backend didn't actually switch
Test cases:
- ✅ Normal switch: updates and verifies successfully
- ✅ API failure: doesn't change state
- ✅ fetchProfiles failure: assumes success (API returned 200)
- ✅ Backend verification fails: rolls back to old profile
This ensures frontend state always matches backend reality, even in
edge cases where hermes profile use succeeded but gateway/cleanup
steps failed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refine: add user feedback for profile operations
Improve user experience with success/error messages:
- ProfileSelector: Add error message when switch fails
- ProfileCard: Add success message before reload on switch
- ProfileSelector: Use async/await for better error handling
- ProfileCard: Add 500ms delay before reload to show success message
Before: Silent failures, no feedback
After: Clear success/error messages for all operations
Example feedback:
- Success: "已切换到配置 qinghe"
- Failure: "切换配置失败,网关可能需要手动重启"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: update frontend changelog for v0.5.7 (#419)
* docs: update frontend changelog for v0.5.7
- Update changelog.ts with v0.5.7 release date and changes
- Add i18n translation keys for all languages (en, zh, de, es, fr, ja, ko, pt)
- Include v0.5.7 changelog entries:
- Optimize context compression and session sync
- Add startup delays to prevent database race conditions
Changes:
- packages/client/src/data/changelog.ts: Update v0.5.7 entry
- packages/client/src/i18n/locales/*.ts: Add changelog translation section
This enables the changelog modal in the UI to display v0.5.7 release notes.
* feat: add v0.5.7 changelog translations to all supported languages
Add new_0_5_7_1, new_0_5_7_2, and new_0_5_7_3 changelog entries to all
locale files (en, zh, de, es, fr, ja, ko, pt) with proper translations
for each language.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: remove duplicate changelog sections causing syntax errors
Remove duplicate changelog object sections that were causing TypeScript
syntax errors in all locale files (en, zh, de, es, fr, ja, ko, pt).
The actual changelog entries are already correctly placed in the main
changelog section of each file.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add v0.5.8 changelog and fix profile parsing issue
Add v0.5.8 changelog entries based on PRs merged since v0.5.7:
- Drawer panel with mobile sidebar support (#412)
- Profile switching state sync fix (#414)
- Speech playback special character filtering (#409)
- Missing i18n key and session data source unification (#408)
- Vite build optimization for faster Docker builds (#403)
Also fix issue #417: Profile names with long hyphenated names fail
to parse in profile list regex. Change \s{2,} to \s+ to handle
compressed column spacing when profile names are long.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: remove enter key submit from profile creation and rename modals
Remove @keyup.enter handlers from NInput components in:
- ProfileCreateModal: prevent accidental profile creation when pressing enter
- ProfileRenameModal: prevent accidental profile rename when pressing enter
Users must now explicitly click the confirm button to submit, preventing
unintended profile operations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: allow free text input for profile names
Remove frontend character filtering from profile creation and rename
modals. Users can now input any characters including spaces and
uppercase letters to test backend Hermes CLI validation.
Changes:
- ProfileCreateModal: Remove toLowerCase() and character filtering
- ProfileRenameModal: Remove toLowerCase() and character filtering
- Use v-model:value binding instead of :value with @input
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: improve error handling for profile creation
Display backend error messages when profile creation fails instead of
generic "failed" message. This helps users understand why their
profile name was rejected (e.g., invalid characters).
Changes:
- API layer: Capture and return error messages from backend
- ProfileCreateModal: Display specific error message from backend
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add profile name validation with i18n support
Add client-side validation for profile names to prevent invalid input
before sending to backend. Only lowercase letters, numbers, underscores,
and hyphens are allowed.
Changes:
- ProfileCreateModal: Add input validation with real-time feedback
- ProfileRenameModal: Add input validation with real-time feedback
- Add nameValidation i18n key for all 8 languages
- Filter invalid characters on input and show warning message
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: revert profile parsing regex changes
Revert the regex changes in hermes-cli.ts and gateway-manager.ts
back to requiring \s{2,} (at least 2 spaces). Since frontend now
validates profile names to only allow lowercase letters, numbers,
underscores, and hyphens, the relaxed regex is no longer needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: revert profile parsing regex changes
Revert the regex changes in gateway-manager.ts and hermes-cli.ts
back to requiring \s{2,} (at least 2 spaces). Since frontend now
validates profile names to only allow lowercase letters, numbers,
underscores, and hyphens, the relaxed regex is no longer needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: remove tooltip from drawer button
Remove the NTooltip wrapper from the floating drawer button.
The "Terminal & Files" tooltip is no longer shown on hover.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update assets images (#421)
Updated two asset images in the client package.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: optimize context compression and session sync
Context Compressor:
- Add ContentBlock type support for user messages with images/files
- Enhance buildConversationHistory to handle ContentBlock[] format
- Update serializeForSummary to convert ContentBlock[] to text
- Support mixed content (text, images, files) in history compression
Session Sync:
- Replace loop of addMessage with batch addMessages (transaction-protected)
- Use addMessages for atomic bulk insert with BEGIN/COMMIT
- Ensure data consistency before calcAndUpdateUsage reads DB
Chat Run Socket:
- Remove convertHistoryFormat before compression (compressor handles raw format)
- Compressor now processes original messages for better tool message handling
- Improve compression quality by preserving original message structure
Benefits:
- Better compression quality with proper tool message recognition
- Improved performance with batch DB inserts
- Strong data consistency with transaction protection
- Support for rich content (images/files) in conversation history
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: bump version to 0.5.7
- Prepare for release with context compression and session sync optimizations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: add 0.5.7 changelog entries
- Optimize context compression to support rich content (images, files)
- Improve session sync with batch inserts and transaction protection
- Fix usage.updated event reception for accurate token tracking
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Redesign attachment handling using Anthropic-style ContentBlock array format
with discriminated unions (text, image, file types)
- Add frontend file download functionality supporting both ContentBlock
and Markdown formats with authentication tokens
- Fix multi-process conflict causing SQLite database resets by eliminating
redundant nodemon instances
- Update chat store to build ContentBlock arrays from attachments
- Improve image handling with base64 conversion for upstream API
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a collapse button at the top of the left sidebar that toggles
between full-width (240px with icons + labels) and compact icon-only
mode (64px). The collapse state is persisted to localStorage and
survives page refreshes.
Changes:
- Add collapse toggle button with chevron icon in sidebar
- Add CSS rules for .sidebar.collapsed: narrow width, centered icons,
hidden labels/group-headers/footer-text
- Leverage existing store state (sidebarCollapsed, toggleSidebarCollapsed)
and SCSS variable ($sidebar-collapsed-width) that were already defined
but never wired up in the template
This significantly improves browsing experience on tablets and
handheld devices by freeing up horizontal space when the full
sidebar is not needed.
Features:
- Add dedicated History page for browsing Hermes session history
- Independent session state (does not interfere with active chat)
- Auto-select first CLI session on page load
- Filter out api_server and cron sources
Components:
- New HistoryView.vue with isolated state management
- New HistoryMessageList.vue with session prop support
- Filters empty content and tool messages without toolName
Backend:
- Add GET /api/hermes/sessions/hermes endpoint (excludes api_server)
- Add GET /api/hermes/sessions/hermes/:id endpoint (404s for api_server)
- Add fetchHermesSessions() and fetchHermesSession() API functions
Cleanup:
- Remove localStorage session caching
- Simplify profile switching cache management
- Clean up废弃 cache cleanup calls
i18n:
- Add "History" translation to all 8 locales
- Add v0.5.5 changelog entries in all languages
- 🎉 Happy Labor Day!
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Added changelog entries for version 0.5.4 covering:
- Fixed concurrent chat sessions event cross-talk with WebSocket event routing refactoring
- Fixed cron job edit payloads with partial PATCH to support long prompt name-only edits
- Fixed web terminal Hermes CLI availability after Docker deployment
- Added workspace dialog i18n translations for title and improved session persistence
- Supported code block copy feedback with user notifications
- Aligned usage analytics with Hermes state DB schema
Updated all language files (en, zh, de, es, fr, ja, ko, pt) and changelog data file.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Refactored the WebSocket event handling mechanism to use global listeners with session-specific event routing instead of per-session listeners. This prevents event cross-talk when multiple chat sessions run concurrently.
Key changes:
- Client: Added sessionEventHandlers Map to route events to appropriate sessions
- Client: Registered global listeners once per socket connection
- Server: Extracted message processing logic into handleMessage method
- Server: Improved Hermes session ID tracking with dedicated Map
- Server: Added replaceByHermesSessionId for targeted message replacement
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: per-session workspace with folder picker, HERMES_HOME support, esbuild fix
* fix(chat): isolate concurrent session events and workspace dialog i18n
Two user-visible bugs are fixed here:
1. Workspace dialog title showed the raw i18n key 'chat.setWorkspaceTitle' because the key was never added to en.ts / zh.ts. The dialog is opened from ChatPanel.vue but only 'setWorkspace' existed. Add the missing 'setWorkspaceTitle' translation in both locales.
2. With two concurrent runs the assistant text from session A would show up in session B (and vice versa). The /chat-run namespace uses a single shared Socket.IO connection on the client; every startRunViaSocket() call registers its own listeners on the same socket. The server fans events out via 'session:<id>' rooms, but a single socket can be in multiple rooms at once and there was no per-event filtering on the client. Each run's closure captured its own sid and wrote into the wrong session. The server already tags every payload with session_id, so the fix is a guard inside handleEvent() that drops events whose session_id does not match this run's body.session_id. Untagged events are still accepted for backwards compatibility.
3. Also fix a related crash where setting a workspace on a session that had not been persisted yet (no first message sent) threw because the row did not exist. Create the row on demand inside setWorkspace controller.
* fix: upgrade esbuild to 0.27+ for vite 8 compatibility
---------
Co-authored-by: ekko <fqsy1416@gmail.com>