* fix: don't drop pending tool-call-marker prefix on tool.started/run.done
The `filterBridgeToolCallMarkupDelta` filter holds back any text that
ends in a partial prefix of `[Calling tool:` (i.e. `[`, `[C`, `[Ca`,
..., `[Calling tool`) so it can decide whether the buffered chars are
the start of a tool-call markup block to be hidden, or just regular
text to be released by the next delta.
The bug: that "release on next delta" assumption breaks at TWO points:
1. **On `tool.started`**: the next chunk for this assistant message is
the tool call itself, NOT a follow-up text delta. Buffered chars
sit there forever and nothing flushes them — they vanish silently
from the user-visible stream.
2. **On run completion**: the code did
`state.bridgePendingToolCallMarkup = undefined` directly, dropping
any pending chars without forwarding them.
Both cases produce the user-visible symptom of "abrupt cuts in text
right before/after tool calls (terminal, read_file, write_file...)" —
1 to 13 characters disappear at exactly the boundary where the model
was emitting natural prose that happened to end with `[`.
The fix introduces `flushPendingToolCallMarkup(state)` and calls it:
- In the `tool.started` branch BEFORE recording the tool call, so the
buffered chars are appended to the open assistant message and emitted
as a normal `message.delta` to the client.
- At run-done BEFORE clearing the buffer, same flush path.
This is a pure recovery patch — no change to the marker detection
logic itself. If the buffer turns out to actually be a real
`[Calling tool: ...]` marker that just hasn't completed yet, that
case is still caught by the existing `markerIdx >= 0` branch in the
filter on the next delta. The only behavioral change is that the
"orphan" cases (text that ends with `[` but never becomes a marker)
are no longer dropped.
* fix bridge marker flush persistence
---------
Co-authored-by: Paulo Cavallari <paulocavallari@users.noreply.github.com>
* feat: add session deep links for chats
* feat: add deep links for history and group chat
* Fix profile-aware session deep links
---------
Co-authored-by: Maxim Kirilyuk <werserk@inbox.ru>
* Allow bridge sessions to run concurrently
* Stabilize bridge concurrency test
* Set bridge approval timeout to 120 seconds
* harden bridge approval concurrency
---------
Co-authored-by: Codex <codex@openai.com>
* fix: recursive skill scan for nested sub-category directories
The Web UI skill scanner (scanSkillsDir) only checked one level deep:
skills/<category>/<subdir>/SKILL.md. Sub-category containers like
mlops/evaluation/ (which has DESCRIPTION.md + subdirs but no SKILL.md)
were skipped entirely, hiding all 12 nested skills under mlops.
Changes:
- scanSkillsDir: extract collectSkills() recursive helper that depth-first
searches for SKILL.md at every level under a category directory.
Directories without SKILL.md but with subdirectories are recursively
descended into.
- listFiles handler: replace hardcoded join(category, skill) path with
recursive findSkillDir() search, so nested skill file browsing works
(e.g., mlops/evaluation/lm-evaluation-harness).
Fixes mlops category showing 1 skill instead of 13. All 20 other
categories verified with zero regression.
* fix: pA handler also needs recursive search for nested skill file content
The readFile_ (pA) handler was constructing direct paths like
skills/category/skill/... which fails for nested sub-category
skills (mlops/evaluation/lm-evaluation-harness). Added fallback
recursive search when direct path returns 404.
Also fixed listFiles (sA) handler to use recursive search for
the same reason - previous fix to dist was not in source TS.
Verified:
- lm-evaluation-harness SKILL.md content: 200 ✅
- vllm SKILL.md: 200 ✅
- huggingface-hub (non-nested): 200 ✅
- reference file in nested skill: 200 ✅
* fix: pA handler also needs recursive search for nested skill file content
The readFile_ (pA) handler was constructing direct paths like
skills/category/skill/... which fails for nested sub-category
skills (mlops/evaluation/lm-evaluation-harness). Added fallback
recursive search when direct path returns 404.
Also fixed listFiles (sA) handler to use recursive search for
the same reason - previous fix to dist was not in source TS.
Verified:
- lm-evaluation-harness SKILL.md content: 200 ✅
- vllm SKILL.md: 200 ✅
- huggingface-hub (non-nested): 200 ✅
- reference file in nested skill: 200 ✅
* harden recursive skill lookup
---------
Co-authored-by: gs <gs@localhost>
Co-authored-by: gutanulaif <gutanulaifa@gmail.com>
* feat: support profile-aware group chat bridge flows
* feat: route cron jobs through hermes cli
* Fix group chat routing and isolate bridge tests
* Add Grok image-to-video media skill
* Default Grok videos to media directory
* Fix bridge profile fallback and cron repeat clearing
* Refine bridge chat and gateway platform handling
* Filter bridge tool-call text deltas
* Preserve structured bridge chat history
* Prepare beta release build artifacts
* Fix Windows run profile resolution
* Fix Windows path compatibility checks
* Fix profile-scoped model page display
* Hide Windows subprocess windows for jobs and updates
* Hide Windows file backend subprocess windows
* Avoid Windows gateway restart lock conflicts
* Treat Windows gateway lock as running on startup
* Force release Windows gateway lock on restart
* Tighten Windows gateway lock cleanup
* Update chat e2e source expectation
* Bump package version to 0.5.30
---------
Co-authored-by: Codex <codex@openai.com>
Revert the dynamic import() for i18n locales and highlight.js core+
registration from #696. Dynamic imports create separate chunk files
that cause 404 errors for users after updating when the browser still
references old chunk hashes.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
When starting gateways, automatically switch to 'default' profile if
current profile is not 'default'. This ensures consistent gateway startup
behavior and prevents port conflicts from different profile configurations.
Changes:
- Check current profile before starting gateways
- Execute 'hermes profile use default' if needed
- Wait 2 seconds for profile switch to take effect
- Update internal GatewayManager state
- Add detailed logging for the switching process
* feat(bridge): refactor compression to use DB history and add structured logging
- Extract buildDbHistory() to share message loading between buildCompressedHistory and forceCompressBridgeHistory
- forceCompressBridgeHistory now reads from local DB instead of using Python-provided messages, ensuring consistency with api_server path
- Pass sessionId to compressor for snapshot-aware compression
- Add force_compress flag to bridge chat requests
- Add bridgeLogger structured logging for compression lifecycle
- Simplify schemas, session-sync, and providers
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix bridge compression history handling
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat: add CLI chat sessions with Python agent bridge
Introduce a new CLI chat mode that connects Web UI directly to Hermes
Agent's AIAgent via a Python bridge subprocess and Socket.IO, bypassing
the API Server /v1/responses path. Supports streaming, slash commands
(/new, /undo, /retry, /branch, /compress, /save, /title), interrupt,
and steer.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat: update CLI chat session bridge
* fix: extend agent bridge startup timeouts
* docs: update bridge chat session design
* feat: align bridge compression and provider registry
* chore: bump version to 0.5.20
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
The regex `^(\w[\w\s]*?)` could not match keys containing dots (`.env`,
`SOUL.md`), so `hasEnv` and `hasSoulMd` were always `undefined` → false.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Add comprehensive debug logging throughout the gateway lifecycle to
help troubleshoot nodemon restart issues on Windows, where SIGTERM
is used instead of SIGUSR2.
Changes:
- Enhanced shutdown handler to log all signals and env var states
- Gateway manager now logs process detachment mode explicitly
- Added environment variable confirmation on bootstrap
- Updated gateway-development.md with new debug logs and troubleshooting steps
Benefits:
- Easier troubleshooting of gateway lifecycle issues
- Clear visibility into signal handling during nodemon restarts
- Better cross-platform development experience
- Production behavior remains unchanged
Testing:
- ✅ Windows: Gateways persist across nodemon restarts
- ✅ macOS/Linux: Existing SIGUSR2 behavior preserved
- ✅ Production: Default shutdown cleanup unchanged
- ✅ Backward compatibility: No breaking changes
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Set HERMES_WEB_UI_STOP_GATEWAYS_ON_SHUTDOWN=0 in nodemon.json so
nodemon restarts don't kill running gateways. Production behavior
unchanged (stops owned gateways by default).
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
- Refactor port detection into reusable getListeningPids/killListeningPids
- Add ss command fallback when lsof is unavailable
- Extract readPidFile for cleaner PID file reading
- Remove unnecessary PowerShell candidate from Windows shell detection
- Add PID validity check in gateway_state.json fallback (Number.isFinite + > 0)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(docker): merge two-container setup into single container
The Web UI already manages the Hermes Agent gateway lifecycle internally
via GatewayManager (spawn hermes gateway run --replace), making the
separate hermes-agent container redundant. The Dockerfile is built on
the hermes-agent base image, so all CLI tooling is already included.
- Remove hermes-agent service and shared volume from docker-compose.yml
- Remove gateway port mapping (8642-8670) — internal-only now
- Update docs/docker.md, README.md, README_zh.md for single-container setup
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs: remove version tag from image references
Use ekkoye8888/hermes-web-ui instead of ekkoye8888/hermes-web-ui:latest
to avoid pinning a specific version in documentation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>