Commit Graph

29 Commits

Author SHA1 Message Date
ww 97fbfac2d6 fix(chat): 检测并提示被静默吞掉的 run 错误 (#206)
当上游 hermes-agent 的 agent 层捕获了 LLM 错误(如无效 API key、
模型不被 provider 支持)后,gateway 仍会发送 run.completed 事件,
但 output 为空、usage 为零。前端因此误认为成功,UI 完全无任何
错误提示。

本次修复在 chat store 的 SSE 处理中加入两个 per-run 标志:
- runProducedAssistantText:仅 reasoning/thinking/message.delta 置位
- runHadToolActivity:仅 tool.started/tool.completed 置位

run.completed 时:
1. 兜底渲染:若本次 run 没有 assistant 文本但 evt.output 非空,
   将 output 作为 assistant 消息落盘(兼容某些只在 completed
   事件给出最终文本的 provider)。
2. 静默错误检测:若三者均不成立(无 assistant 文本 + 无 tool
   活动 + output 空),追加一条 system 消息提示用户检查
   hermes-agent 日志。usage=0 不再作为强条件,避免某些 provider
   或本地模型不返回 usage 时的误报。

api/hermes/chat.ts 给 RunEvent 增加 output 字段类型。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-25 16:21:07 +08:00
ww 369001824e feat(chat): 支持思考块实时流式与历史展示 (#191)
* feat: 添加文件下载功能,支持多 Terminal Backend

实现基于 FileProvider 抽象的文件下载能力,支持 local、Docker、SSH、
Singularity 四种 backend。

主要变更:
- 新增 FileProvider 接口及四种后端实现(含 SSH 命令注入防护)
- 新增 GET /api/hermes/download 下载路由(含 MIME 类型检测)
- 前端 Markdown 文件链接拦截下载 + 附件下载按钮
- 中英文 i18n 翻译
- 更新 README、CLAUDE.md 和设计文档

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: 添加文件浏览器与下载功能,支持目录浏览、文件编辑、预览和上传

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* build: add prepare script so 'npm install git+url' auto-builds dist/

Allows installing this package directly from git without a pre-built dist/.
When cloned via npm, prepare runs 'npm run build' if dist/ is missing,
producing the artifacts declared in the files[] field before packing.

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>

* i18n: backfill files/download translations for de, es, fr, ja, ko, pt

Add nav.files, files.* (39 keys), and download.* (9 keys) so the file
browser UI is fully localized in these six locales instead of falling
back to English.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(files): close preview when navigating or affected file changes

Opening a preview and then navigating directories, deleting the
previewed file, or renaming it left the preview pane stuck on stale
content because previewFile was never cleared.

- stores/hermes/files.ts:
  - fetchEntries clears previewFile on path change (in-place refresh
    keeps the preview).
  - deleteEntry / renameEntry clear preview/editor state when the
    affected entry matches the previewed/edited file or its parent.
  - Add isAffected(target, changed, isDir) helper.
- components/hermes/files/FilePreview.vue: replace the misleading
  common.cancel close button with a dedicated files.closePreview key
  plus an X icon and quaternary style.
- i18n: add files.closePreview to all 8 locales.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: 清理已完成功能的计划与设计文档

文件浏览器与文件下载功能均已被上游合并,对应的开发计划
与设计稿不再需要在 fork 中保留:
- plans/2025-07-20-file-browser.md
- plans/2026-04-20-file-download.md
- specs/2025-07-20-file-browser-design.md
- specs/2026-04-20-file-download-design.md

清理后本 fork 与 upstream/main 代码层面完全对齐。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs: 添加 thinking 块分离与折叠展示设计稿(#164)

针对上游 issue #164,设计 assistant 消息中 <think>/<thinking>/<reasoning>
标签的识别、分离与可折叠展示方案。

关键决策(经 rubber-duck 审查修订):
- 不修改 Message.content 与持久化字段,确保 localStorage 向前兼容
- 耗时摘要改为纯运行时派生(store 内 Map),避免刷新/重连丢失
- 首版即实现代码块保护,避免误识别
- 流结束时未闭合标签降级为正文,防止吞答案
- 解析 computed 与 duration interval 分离,规避性能风险
- 解析器放置 packages/client/src/utils/ 避免反向依赖
- 显式不支持同名嵌套(罕见场景文档化)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs: 添加 thinking 块分离与折叠实施计划(#164)

12 Task TDD 计划:
- Task 1-7:utils/thinking-parser.ts 纯函数模块 + 单元测试
- Task 8-9:chat store thinkingObservation Map 接入 SSE
- Task 10:8 语言 i18n 新增 6 条 key
- Task 11:MessageItem.vue 渲染折叠 UI + SCSS
- Task 12:构建/测试/手动验证/推送

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(thinking-parser): 首个闭合 <think> 标签拆分

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(thinking-parser): 覆盖多段/变体标签/大小写/空输入

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(thinking-parser): 流式 pending 与终止态降级

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(thinking-parser): 代码块保护避免误识别伪标签

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(thinking-parser): 同名嵌套与 chunk 边界行为

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(thinking-parser): countThinkingChars 辅助函数

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(thinking-parser): detectThinkingBoundary 边界检测

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(chat-store): 新增 thinkingObservation 运行时 Map

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(chat-store): message.delta 写入 thinking 边界 + switchSession 清理

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* i18n: 新增 thinking 块 6 条 key(8 语言)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(chat): MessageItem 渲染 thinking 折叠区

- 复用 tool-line 风格 chevron
- 两条响应链:parse computed + duration interval
- 流式+pending 强制展开
- show_reasoning 控制默认态

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(chat): 支持思考块实时流式与历史展示

- 扩展 Message 接口增加 reasoning 字段,mapHermesMessages 从
  HermesMessage.reasoning 透传历史会话的思考内容。
- RunEvent 类型新增 text 字段,chat store 处理三个新 SSE 事件:
  reasoning.delta / thinking.delta / reasoning.available。
- 思考时长观察:仅在 reasoning.delta 累积时记录起始时间戳,
  reasoning.available 时记录结束时间戳;无实时 delta 时不显示时长。
- MessageItem 采用双源渲染(reasoning 字段优先,<think> 标签作
  fallback),duration > 0 才展示耗时。
- 新增 3 条单测覆盖三个 SSE 事件;测试 32/32 通过。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(chat): reasoning 块不再短暂展示正文

根因:上游 hermes-agent run_agent.py:11275 在每次模型响应结束时用
assistant content[:500] 作为 reasoning.available 的 preview 负载,
致使 Web UI 把正文写入 last.reasoning,思考块短暂显示正文直到会话
轮询/刷新从 session DB 读回正确的 reasoning 字段。

修复:
- reasoning.available 事件不再写入 last.reasoning,仅用于标记计时
  结束(noteReasoningEnd);真实推理由 reasoning.delta 或会话 DB
  提供
- 新增 scrubBuggyReasoningInCache:hydration 时治愈 localStorage 里
  已被污染的 assistant 消息(reasoning == content 或前缀时丢弃)
- 两个 cache 加载入口(loadSessions / switchSession)均接入 scrubber

测试:新增 4 条单测,全套 280/280 通过。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-25 08:46:50 +08:00
ekko 833fe06f92 fix: restore fetchAvailableModels to fix provider lost as custom (#194)
* fix: restore fetchAvailableModels to fix provider lost as custom (#174)

#174 replaced fetchAvailableModels with fetchConfigModels, but
config/models response lacks default_provider, label, base_url and
api_key fields. This caused selectedProvider to always be empty,
making all models appear as "custom" in the model selector.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: bump version to 0.4.6

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 23:30:34 +08:00
ekko 70ed0e0dc2 revert: harden Hermes stream recovery around tool-call boundaries (#189) (#192)
Reverts #189 due to reported bugs.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 22:18:32 +08:00
Zhicheng Han 009acc1c28 fix: harden Hermes stream recovery around tool boundaries (#189) 2026-04-24 21:42:42 +08:00
ekko ba72264542 feat: group chat session lifecycle, typing recovery, mention highlighting (#186)
* 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>
2026-04-24 20:41:14 +08:00
ekko f8283729ba refactor: replace jobs proxy with local controller and optimize model loading (#174)
* refactor: replace jobs proxy with local controller and optimize model loading

- Add local jobs controller that directly fetches upstream gateway with
  profile support and 30s timeout, replacing unreliable proxy catch-all
- Upstream errors (non-200) return 502 instead of leaking to frontend
- Switch loadModels() from fetchAvailableModels (slow, fetches all
  provider APIs) to fetchConfigModels (reads config.yaml only)
- Hide logo dance video in sidebar

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve TypeScript errors from previous refactor

- Remove unused imports (danceVideo, useTheme) in AppSidebar
- Map ConfigModelsResponse.groups to AvailableModelGroup[] format

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 09:57:30 +08:00
ekko 88c7e25f78 fix(i18n): add i18n support for custom model feature in ModelSelector (#172)
* feat(models): add custom model name input with provider selector

- Add custom model input field at bottom of model selector modal
- Add provider dropdown to specify target provider for custom model
- Track custom models in app store and display with CUSTOM badge
- Merge custom model into provider group list
- Fix custom provider models being overwritten by API response (keep both)

* Upload screenshot

* fix(i18n): add i18n support for custom model feature in ModelSelector

Replace hardcoded English strings (CUSTOM badge, placeholder, hint) with
vue-i18n t() calls and add corresponding translation keys to all 8 locales
(en, zh, ja, ko, fr, es, de, pt).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: toller892 <892@users.noreply.github.com>
Co-authored-by: Tony <125938283+toller892@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 08:49:45 +08:00
ww a4bfd8edd3 fix(files): close preview on navigation/delete/rename + backfill i18n (#150)
* i18n: backfill files/download translations for de, es, fr, ja, ko, pt

Add nav.files, files.* (39 keys), and download.* (9 keys) so the file
browser UI is fully localized in these six locales instead of falling
back to English.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(files): close preview when navigating or affected file changes

Opening a preview and then navigating directories, deleting the
previewed file, or renaming it left the preview pane stuck on stale
content because previewFile was never cleared.

- stores/hermes/files.ts:
  - fetchEntries clears previewFile on path change (in-place refresh
    keeps the preview).
  - deleteEntry / renameEntry clear preview/editor state when the
    affected entry matches the previewed/edited file or its parent.
  - Add isAffected(target, changed, isDir) helper.
- components/hermes/files/FilePreview.vue: replace the misleading
  common.cancel close button with a dedicated files.closePreview key
  plus an X icon and quaternary style.
- i18n: add files.closePreview to all 8 locales.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 14:10:14 +08:00
ekko 1abe308742 feat: add Node.js version warning, fix provider URL detection, and add v0.4.4 changelog (#146)
- 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>
2026-04-23 12:57:42 +08:00
ww 0cc31ee999 feat: add file browser and file download with multi-backend support (#142)
* 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>
2026-04-23 12:09:39 +08:00
Zhicheng Han 5f40ae6258 feat(chat): add direct Live badge and harden Live monitor backend (#138)
* feat(chat): add direct live badge to session rows

* fix(live): use session DB for conversations monitor

* docs: add chat vs live monitor direction plan

* fix(search): avoid numeric session search 500 without FTS table
2026-04-23 10:49:00 +08:00
mysoul12138 696d19298e fix: avoid localStorage quota errors when switching chats\n\n- keep default profile legacy cache read compatibility\n- stop duplicating bulky session/message/in-flight cache into legacy keys\n- add best-effort quota recovery before persisting active session\n- cover legacy cache migration in chat store tests (#137) 2026-04-23 07:35:05 +08:00
ekko 6f69c69802 feat: add token usage tracking, context display, and dynamic context length (#132)
* 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>
2026-04-22 16:14:50 +08:00
cl1107 f27db3036a feat: add session search modal (#128) 2026-04-22 14:00:34 +08:00
Zhicheng Han 3f88553765 feat(web-ui): add pinned sessions and live monitor in Chat (#118)
* 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>
2026-04-22 08:09:58 +08:00
ekko d3817556ac feat: sort sessions by latest message time instead of creation time
Use `last_active` from SQLite (max message timestamp) for accurate
sorting, with fallback chain: last_active → ended_at → started_at.
CLI mode lacks last_active so falls back to ended_at.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 23:32:01 +08:00
ekko 72f238703d Merge branch 'codex/active-session-live-state' into dev 2026-04-19 22:52:41 +08:00
mysoul12138 7c4b025e6a refine active session live state 2026-04-19 21:51:25 +08:00
ekko 562261d13f feat: multi-gateway profile support, provider management overhaul, and model settings tab
- 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>
2026-04-19 20:59:25 +08:00
ekko 8a4ab2d69d fix: provider-aware model selection to prevent cross-provider conflicts
When multiple providers share the same model name, the selector now
uses both model ID and provider as the unique identifier instead of
model name alone. Backend returns default_provider alongside default
model, and model switching sends provider to the config.

Fixes #52

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 15:05:05 +08:00
ekko 27051dcb32 feat: profile-aware cache isolation and UX improvements
- Fix chat store cache keys to include profile name, prevent data leaking between profiles
- Defer cache hydration to after profile load to avoid race condition
- Remove collapsible sidebar feature (not needed)
- Remove confirmation dialog on profile switch (direct reload)
- Auto-start gateway when creating new profile
- Clear profile-specific localStorage cache on profile delete (safe prefix matching)
- Clean up unused imports in SettingsView

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 14:32:54 +08:00
ekko 17f0cdc1de Merge branch 'pr-44' into feat/multi-gateway
# Conflicts:
#	packages/client/src/components/layout/AppSidebar.vue
2026-04-18 13:12:42 +08:00
ekko 4b6de351bd feat: add multi-gateway management with auto port detection
- 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>
2026-04-18 13:07:12 +08:00
zksnet 7e777fd661 feat(chat): improve resilience and collapsible sidebar
问题描述:\n- 刷新页面、切后台或手机锁屏后,进行中的对话容易丢失,SSE 断开时前端还会插入假的错误气泡\n- 移动端首屏会话列表会短暂遮住聊天区\n- 桌面端侧栏无法折叠,在窄窗口和缩放场景占用过多横向空间\n\n复现路径:\n- 发起一轮对话,在模型仍在输出时刷新页面或锁屏后再回到页面\n- 在窄屏设备首次打开聊天页,观察会话列表首帧覆盖聊天内容\n- 在桌面端缩窄浏览器窗口,观察侧栏始终保持完整宽度\n\n修复思路:\n- 为 chat store 增加本地缓存、水合、in-flight 标记和轮询恢复,SSE 断开后静默从服务端回补真实结果\n- 将运行中指示统一到 isRunActive,让实时流式与恢复轮询共享同一状态\n- 在 ChatPanel 首帧同步读取媒体查询,避免移动端会话列表闪烁覆盖\n- 为侧栏增加可持久化的桌面折叠状态,并补充对应文案与回归测试
2026-04-18 00:00:24 +08:00
ekko 26423984d1 fix: profile import file upload, startup health check, sidebar scroll, node-pty fallback
- Change profile import from server path input to browser file upload (multipart)
- Fix startup script to wait for health check before opening browser
- Add overflow scroll with hidden scrollbar to sidebar nav
- Graceful degradation when node-pty fails to load (WSL compatibility)
- Remove rename button from profile cards
- Restrict profile name input to English letters, numbers, hyphens
- Use raw.githubusercontent.com URLs in README setup script

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 15:19:05 +08:00
ekko 99a47cf1ad feat: profile-aware routes, provider sync, channel settings improvements
- 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>
2026-04-16 13:51:42 +08:00
ekko 04b80a616e feat: add profile management page with full CRUD UI
- Add frontend API layer, Pinia store, and 5 components (ProfileCard, ProfilesPanel, ProfileCreateModal, ProfileRenameModal, ProfileImportModal)
- Add ProfilesView page with card grid layout and expandable details
- Modify export endpoint to stream file as browser download instead of returning server path
- Add sidebar nav entry, router route, and i18n translations (en/zh)
- Support create, rename, delete, switch (with gateway restart), export, and import profiles

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 09:40:25 +08:00
ekko 351c861777 refactor: restructure project for multi-agent extensibility
- Migrate source to packages/client and packages/server directories
- Namespace all Hermes-specific code under hermes/ subdirectories
  (api/hermes/, components/hermes/, views/hermes/, stores/hermes/)
- Add hermes.* route names and /hermes/* path prefixes
- Upgrade @koa/router to v15, adapt path-to-regexp v8 syntax
- Fix proxy path rewriting: /api/hermes/v1/* → /v1/*, /api/hermes/* → /api/*
- Fix frontend API paths to match backend /api/hermes/* routes
- Fix WebSocket terminal path to /api/hermes/terminal
- Add proxyMiddleware for reliable unmatched route proxying
- Add profiles route module and hermes-cli profile commands
- Update CLAUDE.md development guide with new architecture
- Add Chinese README (README_zh.md)
- Add Web Terminal feature to README

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 08:38:18 +08:00