Three follow-ups the user spotted in one round.
assistant.js — assistant did not know it was on Hermes
Both engines (OpenClaw and Hermes Agent) reuse the same /assistant
page (engines/hermes/index.js comments it as "共用页面/引擎无关"),
but getSystemPromptBase() hard-coded the OpenClaw self-introduction:
"你帮助用户管理和排障 OpenClaw AI Agent 平台 / 你精通 OpenClaw 的架
构…", followed by a CLI cheatsheet for `openclaw gateway start` and
`openclaw config apply`. Result: under the Hermes engine, the
assistant happily told users to run `openclaw doctor` and edit
`~/.openclaw/openclaw.json` — neither of which exists in the Hermes
world.
Split into a per-engine dispatcher:
getSystemPromptBase()
└ if hermes → getHermesSystemPromptBase() (new)
└ else → getOpenclawSystemPromptBase() (renamed, same body)
The new Hermes base prompt covers the facts that actually matter:
- dual-process layout: Gateway 8642 (chat API, what ClawPanel
mostly drives) vs Dashboard 9119 (admin/profiles/skills/oauth/
kanban — must be started separately)
- Profile system (independent workspaces, switchProfile restarts
dashboard, multi-gateway view)
- lazy_deps allowlist and why pre-installing matters
- paths: ~/.hermes (data) and ~/.hermes-venv (interpreter), with a
reminder that ~/.openclaw/clawpanel.json is the panel config
shared with the OpenClaw engine — not Hermes data
- Top-5 problem playbook (9119 not running, venv missing, channels
hanging on first launch, gateway crashing, profile drift)
- Explicit "do not give the user `openclaw …` commands"
Two more spots in buildSystemPrompt() are also engine-aware now:
- the "ClawPanel 工具能力" bullet list inside the soul-cache branch
- the "跨平台路径" reminder (Hermes points to .hermes / .hermes-venv)
lazy-deps.js — "请确认目标资源是否仍存在" was masking the real hint
When the user has not installed Hermes yet, Rust's
`hermes_lazy_deps_features` returns the very actionable string
"Hermes venv 未找到(~/.hermes-venv 不存在)。请先安装 Hermes。".
humanize-error.js then sees "未找到", classifies the error as
notFound, and replaces the message with the generic template
"请确认目标资源是否仍存在" — which tells the user nothing about
installing Hermes.
Take humanizeError() but render `message + raw` instead of
`message + hint`. The user now sees both the friendly title and the
exact Rust-side instruction. Drop the unused humanizeErrorText
import that this commit replaces.
config.rs — unblock CI (clippy too_many_arguments on existing code)
The clippy gate has been red on main since e1eda2d ("import external
client configs") because two helpers in commands/config.rs take >7
positional parameters:
- push_client_candidate (14 params)
- scan_json_client_file (10 params)
Both helpers exist purely to push a flat record into a Vec<Value>.
Wrapping them in a struct just to satisfy clippy would force every
caller to first build that struct, hurting readability. Suppress
clippy::too_many_arguments locally on these two functions with an
inline comment explaining why.
## Verification
- node --check + npm run build: clean
- cargo clippy --all-targets -- -D warnings: now compiles to
"Finished `dev` profile" with zero errors/warnings (previously
failed with two too_many_arguments)
- Playwright: import lazy-deps with api.hermesLazyDepsFeatures mocked
to throw "Hermes venv 未找到 … 请先安装 Hermes。", rendered content
contains "请先安装 Hermes" (hasRaw=true), does not contain the
generic "请确认目标资源是否仍存在" (hasGenericNotFound=false), and
does not contain "[object Object]"
Users have reported confusion about "when will ClawPanel update its
gateway protocol to v4". This is actually a misreading: ClawPanel v0.15+
already advertises `minProtocol=3, maxProtocol=4` in its connect frame,
and negotiates v4 transparently when the kernel is >= 2026.5.12. The
`v3|` prefix users were seeing in dev-api.js is the device signature
payload string schema version, which is a completely separate concept
from the handshake protocol version.
Make this visible and unambiguous:
UI
- Add a "Proto v4" badge next to the Gateway service name in
/services once the WS handshake succeeds, with a tooltip explaining
that this is the WS handshake protocol version (not the device
signature payload v3 format).
- Add the same protocol info to the WebSocket row in /chat-debug.
API
- WsClient now exposes `negotiatedProtocol` which prefers the explicit
field from the hello payload (`protocol` / `protocolVersion` /
`negotiatedProtocol`) and falls back to inferring from serverVersion:
kernels >= 2026.5.12 are reported as v4, older as v3. This matches
the panel's advertised range of [3, 4].
- KernelSnapshot grows a `protocol` field so feature gates and UIs that
already consume the snapshot can read it without touching wsClient.
Comments
- Expand the KERNEL_TARGET comment in feature-catalog.js to spell out
the two-distinct-version-numbers rule explicitly.
- Add matching clarifying comments next to the `v3|...` payload string
in both scripts/dev-api.js and src-tauri/src/commands/device.rs, so
the next reader does not confuse payload schema with handshake.
## Verification
- node --check on all touched JS files
- npm run build
- cargo fmt --check && cargo check (clippy errors that surface are
pre-existing debt in config.rs, untouched here)
- Playwright /services: mock wsClient state, observe `协议 v4` badge
rendered with `rgba(99, 102, 241, 0.1)` background and accent color,
for both the explicit-protocol path and the version-inferred path.
The previous implementation passed CREATE_NEW_CONSOLE to a Rust
StdCommand spawning cmd.exe directly, but Rust's default Stdio::inherit
copies the parent stdio handles into STARTUPINFO with
STARTF_USESTDHANDLES, which neutralizes CREATE_NEW_CONSOLE. The cmd
process then ran without a visible window (MainWindowHandle = 0), so
users only saw the OpenClaw node child started by runner.cmd in Task
Manager and got the impression that "the terminal does not pop up".
Wrap the launch in `cmd /c start "OpenClaw Gateway" /D <dir> cmd /D /K
runner.cmd` so the new console is created by the `start` builtin via a
fresh CreateProcess call without inherited stdio. The outer cmd /c
itself is short-lived and uses CREATE_NO_WINDOW so it does not flash a
window, and the inner cmd hosting runner.cmd reliably becomes a normal
visible terminal that the user can close to stop Gateway.
Because the visible terminal is now detached from our process tree,
spawn().id() can no longer be tracked. Instead, poll netstat after the
launch and record the listener PID as the active Gateway PID, which is
what the stop path actually needs to send a precise kill to.
## Verification
- cargo fmt --manifest-path src-tauri/Cargo.toml --all -- --check
- cargo check --manifest-path src-tauri/Cargo.toml
Add a model client import flow that scans local Codex, Claude Code, Gemini CLI, and common environment variable configurations without reading or copying OAuth tokens.
The new backend command returns safe import candidates with provider metadata, model IDs, and API key environment-variable references. Tauri and Web/dev-api both implement the scanner, and Web mode keeps the scan local even when a remote instance is active.
The Models page now offers an import wizard that lets users select importable candidates, adds providers without overwriting existing keys, preserves secrets as ${ENV_VAR} references, and leaves OAuth-only Codex entries as guidance rather than direct OpenClaw imports.
## Verification
- node --check src/pages/models.js
- node --check src/lib/tauri-api.js
- node --check src/locales/modules/models.js
- node --check scripts/dev-api.js
- cargo fmt --check
- cargo check
- npm run build
Use a generated clawpanel-gateway.cmd runner for the visible Windows Gateway terminal instead of embedding the resolved CLI path directly in the cmd /K command string. This keeps the simple visible-terminal model while improving quoting behavior for standalone, npm shim, and .js CLI paths.
## Verification
- cargo fmt --manifest-path src-tauri/Cargo.toml --all -- --check
- cargo check --manifest-path src-tauri/Cargo.toml
- cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets -- -D warnings
- npm run build
Windows users can now start OpenClaw Gateway in a normal terminal window instead of relying on a hidden background process. Keeping the terminal open keeps Gateway running; closing it stops Gateway.
The backend guardian also treats a closed Windows Gateway terminal as an intentional stop, so it will not immediately pop a new terminal back up after the user closes it. macOS and Linux keep their existing startup behavior.
## Verification
- cargo fmt --manifest-path src-tauri/Cargo.toml --all -- --check
- cargo check --manifest-path src-tauri/Cargo.toml
- cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets -- -D warnings
- npm run build
OpenClaw Chinese edition has advanced to 2026.5.12-zh.2 while the panel still recommended 2026.5.12-zh.1 and treated same-base zh patch versions as equivalent.
This updates the recommended Chinese kernel target and makes the version comparison detect suffix-level upgrades such as zh.1 -> zh.2 when both sides expose suffixes. It also adds explicit latest-upstream upgrade actions on the Services and About version cards so users can upgrade to the latest detected upstream version without going through the manual version picker.
## Changes
- update OpenClaw Chinese recommended target to 2026.5.12-zh.2
- detect same-base suffixed patch upgrades in version info
- add Services page "upgrade to latest" action and confirmation copy
- add About page latest-upstream action
## Verification
- npm run build
- cargo check
When the user switches model via the model picker, hermes_update_model rewrites
model.default and model.provider in config.yaml but historically left model.context_length
untouched. The Hermes kernel (8ac351407, May 2026) now actively clears that field on
model switch because the previous model's context window almost never matches the new
model — leaving the stale value caused 'context too large' errors and silently truncated
output. Mirror the upstream behavior by dropping the context_length line as we walk the
model: block, so Hermes falls back to the new model's default window.
Hermes core 1e01b25e7 (May 2026) renamed the display label across the model picker / wizard / status output but kept the provider slug 'alibaba' and the DASHSCOPE_* env vars unchanged. Mirror the label here so the picker label in ClawPanel matches what users see in the Hermes TUI/CLI; existing configs are not migrated because slug and env vars are stable.
- Detect git/network failure patterns (failed to connect, could not resolve host,
unable to access, etc.) in install/update output and append a clear hint
pointing users to the proxy or mirror settings instead of leaving them with
raw multi-line git stderr.
- Add optional 'Hermes Install Mirror' setting (panelConfig.gitMirror): when set,
install/upgrade injects GIT_CONFIG_COUNT/KEY_0/VALUE_0 to rewrite
https://github.com/ via the mirror prefix at process scope only — the user's
global ~/.gitconfig is never touched.
- Surface the new mirror field in Settings (works for both engines), with
zh-CN/en/zh-TW copy and a hint explaining how it interacts with the install
flow.
- /v1/responses ignores body.session_id and generates a fresh server-side
session for each request, causing the Hermes sessions list to grow by
one entry per ClawPanel message.
- /v1/runs honors body.session_id directly, so reversing the call priority
(try /v1/runs first; fall back to /v1/responses on HTTP 404 for older
Hermes Agent builds) lets sessions group naturally without breaking
backward compatibility.
Close the G7 gap from the v3 integration design.
Scenario: A user upgrades Hermes (or manually edits config.yaml and drops
the api_server platform block). The next `gateway start` silently produces
a Gateway that doesn't expose /v1/runs, and ClawPanel's chat/agent flows
fail with confusing errors.
Solution: Run a pre-start guardian that checks
`platforms.api_server.enabled: true` and auto-heals the file when
missing, with a timestamped backup so users can always roll back.
Backend (src-tauri/src/commands/hermes.rs):
- New pure helpers `config_has_api_server_enabled(raw)` and
`patch_yaml_ensure_api_server(raw)` working directly on YAML strings
(no serde_yaml dependency needed — we only touch 3 structural keys).
- `ensure_api_server_enabled(app)` wraps the helpers with filesystem
I/O: reads config.yaml, writes `config.yaml.bak-<epoch>` on mutation,
writes the patched content back, emits `hermes-config-patched` so
the frontend can show a transparent toast.
- Called from `hermes_gateway_action` on every `start` action. If
config.yaml doesn't exist, the guardian is a no-op (configure_hermes
creates a compliant file on first run).
- 7 new unit tests covering: truthy value variants (true/yes/on/1),
missing/disabled/commented scenarios, no-op when healthy,
appending a full platforms block, injecting into existing platforms,
replacing an explicitly disabled api_server subtree.
Web mode (scripts/dev-api.js):
- Mirrors the three helpers as `_hermesConfigHasApiServerEnabled`,
`_hermesPatchYamlEnsureApiServer`, `_hermesEnsureApiServerEnabled`.
- Called in `hermes_gateway_action` start path.
- Logs to console.warn instead of emitting a Tauri event.
Frontend (src/engines/hermes/pages/dashboard.js):
- Dashboard subscribes to `hermes-config-patched` and surfaces a toast
(6s duration) so the user knows the auto-heal happened and where the
backup lives.
Verified: cargo fmt / cargo clippy -D warnings / cargo test all green
(12 hermes tests pass total: 5 provider registry + 7 guardian).
`node --check scripts/dev-api.js` passes. `npm run build` green.
Users may need to configure custom environment variables for Hermes
(e.g. `TAVILY_API_KEY` for the tavily skill, `HTTP_PROXY`, SKILL_*
settings). Previously the only way to set these was to hand-edit
~/.hermes/.env, which risks clobbering the provider keys that
ClawPanel writes through configure_hermes.
This patch adds a dedicated editor UI backed by three new Tauri
commands that refuse to touch managed keys.
Backend (src-tauri/src/commands/hermes.rs):
- `hermes_env_read_unmanaged` — returns every KEY=VALUE pair whose key
is NOT in `hermes_providers::all_managed_env_keys()` (so provider
API keys, base URLs, `GATEWAY_ALLOW_ALL_USERS`, `API_SERVER_KEY`
stay hidden). Preserves file order, dedups.
- `hermes_env_set(key, value)` — validates key against `^[A-Z0-9_]+$`,
refuses managed keys, updates first occurrence or appends,
preserves comments/blanks.
- `hermes_env_delete(key)` — refuses managed keys, removes first
matching line, preserves other structure.
All three commands registered in `src-tauri/src/lib.rs`.
Frontend:
- New page `src/engines/hermes/pages/env-editor.js`:
- Header with "back to dashboard" link and warning banner listing
which keys are managed by ClawPanel.
- Table with Key / Value / Actions columns.
- Inline edit mode per row (save / cancel).
- "Add variable" button for new entries.
- Value column masks long secrets (`sk-a…xyz9`) so glances don't
leak credentials.
- Toast feedback on save / delete / validation errors.
- Inline Chinese copy (TODO: wire up i18n when the locales module
lands).
- Route registered at `/h/env` in `src/engines/hermes/index.js`.
- Dashboard "Model config" section now has a subtle link
".env 高级编辑 →" pointing to the new page.
API wiring:
- `src/lib/tauri-api.js`: added `hermesEnvReadUnmanaged`,
`hermesEnvSet`, `hermesEnvDelete`.
- `scripts/dev-api.js`: mirrors the three commands in Web mode with
a duplicated managed-key list (keep in sync with Rust's
`hermes_providers::all_managed_env_keys` as new providers land).
Verified: cargo fmt / cargo clippy -D warnings / cargo test / npm run
build all green. Dashboard chunk unchanged (24.30 kB); new env-editor
chunk is ~7 kB gzip.
Introduce an authoritative Hermes provider registry in Rust, mirroring
upstream `hermes-agent` data (PROVIDER_REGISTRY + _PROVIDER_MODELS), and
refactor the four critical Hermes commands to be provider-aware.
Closes the G1/G2/G3 blockers identified in the v3 integration design.
New module `src-tauri/src/commands/hermes_providers.rs`:
- Static catalog of 22 providers: 17 api_key (incl. DeepSeek, Gemini,
xAI, GLM/Z.AI, Kimi, MiniMax int'l + CN, Alibaba DashScope, Xiaomi,
Copilot PAT, HuggingFace, OpenRouter, Vercel AI Gateway, OpenCode
Zen/Go, Kilocode), 3 OAuth (Nous, OpenAI Codex, Qwen OAuth), 1
external_process (Copilot ACP), and `custom` placeholder.
- Each entry carries id, display name, auth_type, base_url, env var
priority list, transport, probe strategy, known models, aggregator
flag, and CLI auth hint.
- Helpers: `get_provider`, `primary_api_key_env`, `primary_base_url_env`,
`all_managed_env_keys`, `infer_provider_from_env_keys`,
`find_provider_by_model`.
- New Tauri command `hermes_list_providers` (cached client-side).
- 5 unit tests covering registry integrity and lookup semantics.
Refactored `src-tauri/src/commands/hermes.rs`:
- `configure_hermes`: writes `model.provider` in config.yaml and routes
API keys through the registry's `api_key_env_vars`. Skips key write
for OAuth providers (Hermes CLI manages auth.json itself). Clears
all managed keys on provider switch to avoid stale credentials.
- `hermes_read_config`: reads `model.provider`, then reverse-looks up
the matching API key from the registry's env var priority list.
Falls back to inferring provider from .env when config.yaml omits
the provider field.
- `hermes_update_model`: accepts optional `provider` param; writes
`model.provider` line into config.yaml (adds it when missing, updates
in place when present, removes it for `custom`).
- `hermes_fetch_models`: accepts optional `provider` param; uses
registry's probe strategy (`PROBE_NONE` returns static catalog for
OAuth providers instead of hitting the API).
Registration:
- `src-tauri/src/commands/mod.rs`: declare `hermes_providers` module.
- `src-tauri/src/lib.rs`: import + register `hermes_list_providers`.
Verified: cargo fmt / cargo clippy -D warnings / cargo test all green
(5 new unit tests pass).
* feat(diagnose): detect and inform about @homebridge/ciao cmd popup bug
On Windows, OpenClaw's transitive dependency @homebridge/ciao (<=1.3.6)
calls child_process.exec('arp -a ...') every 15-30 seconds without
passing windowsHide:true, causing a cmd.exe popup to flash.
This is an upstream library bug:
- Issue: homebridge/ciao#64
- PR: homebridge/ciao#65 (open, not merged)
ClawPanel deliberately chooses 'detect and inform' rather than silently
patching the user's node_modules. We respect the user's control over
their own machine.
Changes:
- src-tauri/src/commands/diagnose.rs: new check_ciao_windowshide_bug
command; scans openclaw's @homebridge/ciao/lib/NetworkManager.js and
reports whether the buggy exec pattern is present
- src-tauri/src/lib.rs: register the new command
- scripts/dev-api.js: Web-mode stub (returns affected:false since the
bug does not manifest off-Windows)
- src/lib/tauri-api.js: add api.checkCiaoWindowsHideBug
- src/lib/ciao-bug-warning.js: new module with toast + modal flow,
version-scoped dismiss (localStorage)
- src/locales/modules/ciaoBug.js: translations in 5 primary languages
- src/locales/index.js: register the ciaoBug module
- src/main.js: call checker 3s after splash hides
Non-Windows users see nothing; Windows users see a single warning toast
(version-dismissible) linking to three fix paths: wait for upstream,
apply patch-package, or edit NetworkManager.js manually.
* fix(diagnose): gate helper with cfg(windows), drop unneeded return
CI failures on Linux + macOS:
- openclaw_module_root was dead code when target_os != windows
since the only caller is the #[cfg(target_os = "windows")] block
inside check_ciao_windowshide_bug
- Explicit `return CiaoCheckResult {...};` in the non-Windows branch
triggered clippy::needless_return
Fix:
- Add #[cfg(target_os = "windows")] to openclaw_module_root so it
is not compiled on other platforms
- Convert the non-Windows early exit to a tail expression
Fix for #244 Hidden-start repeat loop:
cleanup_zombie_gateway_processes used a single /health probe to
decide whether a port-listening process is a zombie. When Gateway
reaches 'ready' but is still initializing (loading plugins, connecting
channels, warming up networks), a single probe can time out -> the
healthy Gateway gets killed -> start_service_impl Hidden-starts a new
one -> loop.
Add is_gateway_port_responsive_with_retry: 3 attempts with 800ms
interval before classifying as zombie. Maximum wait 2.4s for a
process that's genuinely healthy to show up as such.
Effect: healthy Gateway no longer misidentified during warm-up,
Hidden-start no longer triggered on an already-ready Gateway.
Refs #244
Root cause for #243 / #244 / #240: model edits trigger
api.restartGateway() with only 300ms debounce. Fast consecutive
edits stack up restart calls, creating zombie Gateway processes,
failed restarts, and CPU fan spikes.
Layer A (frontend):
- New src/lib/gateway-restart-queue.js: 3s debounce + single-flight
lock + reschedule on in-flight request
- Refactor src/pages/models.js doAutoSave: write config immediately,
schedule restart via queue with 'Apply now' toast button
- Subscribe to queue state for unified success/failure toast
- Add i18n: models.configQueued, models.applyNow
Layer B (backend):
- src-tauri/src/commands/config.rs: wrap restart_gateway /
reload_gateway with tokio::sync::Mutex + 2s cooldown
- Cargo.toml: add tokio 'sync' feature
- scripts/dev-api.js: same guard for Web mode (inflight promise
reuse + 2s cooldown)
Effects:
- 10 rapid edits within 3s -> 1 restart (was 10+ with races)
- Backend serializes concurrent restart calls, no zombie spawns
- User sees single 'Apply now' toast instead of restart storm
Refs #243#244#240