* Fix Windows bundled Hermes CLI launcher
* Update kanban service tests for Hermes process wrapper
---------
Co-authored-by: xingzhi <chuzihao.czh@alibaba-inc.com>
Profile avatars are sent to /profiles/:name/avatar as base64 image data
URLs in a JSON body. The handler allows up to 1MB of raw image data,
which is ~1.37MB once base64-encoded — larger than @koa/bodyparser's
default 1mb jsonLimit, so uploads were rejected with HTTP 413 before
reaching the handler.
Set jsonLimit/textLimit to 4mb, leaving the handler's own 1MB raw-size
check as the authoritative limit (now returning a clear 400 instead of
a confusing 413).
Co-authored-by: xingzhi <chuzihao.czh@alibaba-inc.com>
Provider auth controllers derived the credential directory with
`authPath.substring(0, authPath.lastIndexOf('/'))`. On Windows paths use
backslashes, so `lastIndexOf('/')` returns -1 and the slice yields an
empty string, making `mkdirSync('')` throw
`ENOENT: no such file or directory, mkdir ''` during OAuth login.
Replace the manual slicing with the cross-platform `path.dirname()` in
codex-auth (auth.json + codex CLI token paths), xai-auth, and nous-auth.
Fixes Codex/xAI/Nous login on Windows.
Co-authored-by: xingzhi <chuzihao.czh@alibaba-inc.com>
* fix desktop preload fetch typing
* rename desktop app to Hermes Studio
* rename desktop package to Hermes Studio
* update Hermes Studio desktop icons
* configure desktop signing and app id
* force desktop api calls to local server
* isolate desktop agent bridge ports
* bundle MCP support in desktop Python
* change desktop default port to 8748
* restore webui production port copy
* Add desktop packaging workflow
* Add desktop package homepage
* Fix desktop default credential prompt
* Suppress default credential prompt on desktop
* Publish desktop artifacts on release; reduce CI to PR smoke test
Add desktop-release.yml triggered on release publish (mirroring
docker-publish.yml) to build all platforms and upload .dmg/.exe/
.AppImage/.deb to the GitHub Release.
Trim build.yml desktop job to a PR-only Linux x64 smoke test, since
release artifacts are now produced by desktop-release.yml. This drops
per-push and macOS/Windows packaging from regular CI.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Fix desktop Hermes data home on Windows
---------
Co-authored-by: xingzhi <chuzihao.czh@alibaba-inc.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
When the model interleaves narration text with tool calls within one
turn ("text → tool → more text"), the assistant text was rendered split
across the tool boundary — a word could be cut in half, e.g. the part
before the tool call ending mid-word and the remainder appearing after
the tool card.
Root cause: the agent bridge (`hermes_bridge.py`) accumulated streamed
text in `RunRecord.deltas` and tool/lifecycle events in
`RunRecord.events` as two parallel lists with no relative ordering. On
poll, the aggregated text (`"".join(deltas)`) and the events were
delivered separately, and the Node consumer (`handle-bridge-run.ts`)
processed all `chunk.events` (including `tool.started`) before the
aggregated `chunk.delta`. The real interleaving of text and tool calls
was therefore lost, splitting the text around the tool boundary.
Fix:
- Bridge: `stream_callback` now also appends each text chunk as an
ordered `stream.delta` event into the same `events` list as
tool.started/tool.completed, preserving true interleaving. `deltas`
is still kept for the aggregated `output`/resume snapshot.
- Node: process `stream.delta` events inline within the events loop (in
true order), and skip the aggregated `chunk.delta` when ordered
`stream.delta` events were present for that chunk (avoids duplicate
text). Text-delta handling was extracted into `processBridgeTextDelta`
and reused by both paths.
Verified end-to-end: narration that calls a tool mid-sentence now
streams and persists as coherent text in the exact order produced, with
no word split across the tool boundary.
Co-authored-by: Paulo Cavallari <paulocavallari@users.noreply.github.com>
* fix windows coding agent status detection
* fix windows coding agents detection and terminal launch
- Fix Claude Code status detection on Windows by prioritizing .cmd files over unix-style scripts when using 'where' command
- Fix command execution logic for .cmd/.bat files to use proper cmd.exe quoting instead of complex cmdQuote function
- Fix native terminal launch on Windows by properly escaping shellCommand in PowerShell Start-Process instead of using empty $args[0]
These changes resolve issues where Claude Code was incorrectly detected as uninstalled on Windows and native terminal launch failed with PowerShell argument errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix claude code custom model launch
* fix windows filename sanitization for coding agent config paths
- Replace invalid filename characters (< > : " / \ | ? *) with underscores in provider/profile names
- Prevents ENOENT errors when provider names contain Windows-invalid characters like colons
- Fixes issue where 'custom:glm-coding-plan' provider would fail to create config directory on Windows
This change ensures that coding agent configuration paths are valid on all platforms while preserving the semantic meaning of provider names.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* remove stale planning docs
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds an opt-in environment variable to suppress the npm-registry update
check. When set, three things change:
1. checkLatestVersion() returns immediately (no outbound fetch)
2. startVersionCheck() does not arm the 30-minute interval
3. /api/health returns webui_latest='' and webui_update_available=false
Use case: hermes-web-ui is bundled inside a packaged distribution like
a desktop app where the user cannot `npm install -g hermes-web-ui@latest`
to apply an upgrade. The 'update available' prompt is then misleading
(the user would have to wait for the wrapper app to ship a new release),
and the recurring HTTPS call to the npm registry is unnecessary noise.
Set HERMES_WEB_UI_DISABLE_UPDATE_CHECK=true | 1 | on | yes to disable.
The default behavior is unchanged.
Discussed in #1091 — proposed by @EKKOLearnAI.
Move TTS routes behind auth middleware and attach JWT to local
proxy requests from the frontend. Previously both /api/hermes/tts
and /api/tts/proxy/audio/speech were publicly accessible without
authentication, allowing unauthenticated callers to consume Edge
TTS resources through the server.
Changes:
- server: move ttsRoutes from public to protected route section
- client: auto-attach JWT when baseUrl is a local path (/...)
and no external API key is configured
- client: import getApiKey() instead of raw localStorage access
Adds an input field for the DingTalk AI Card template ID under
Platform Settings, plus the matching DINGTALK_CARD_TEMPLATE_ID
env mapping so the value is persisted to the active profile and
forwarded to the hermes agent (which already supports AI Cards
via extra.card_template_id).
Closes#1035
The authStatus() controller previously returned the first users
username to unauthenticated clients. The frontend never used this
value — `fetchAuthStatus()` in LoginView.vue discards the return
value entirely. Remove the field to prevent username enumeration.
Changes:
- server: drop `username` from authStatus response body
- server: remove unused `findFirstUser` import
- client: remove `username` from AuthStatus interface
The hermes-agent CLI KawaiiSpinner sends decorative kaomoji text
like "(◕‿◕✿) pondering..." through thinking_callback for its TUI
widget. The bridge forwarded this as thinking.delta events, which
the frontend stored in the message reasoning field.
Over long conversations this contaminated the model's context:
_copy_reasoning_content_for_api promoted the kaomoji text to
reasoning_content, causing the LLM to reproduce kaomoji patterns
in a self-reinforcing degradation loop.
Fix: _make_thinking_callback unconditionally sends empty text.
thinking_callback is purely CLI spinner status — it has no place in
conversation history. Actual model reasoning (reasoning.delta) is
unaffected.