Skip to content

feat: integrate agent-api-gateway + status indicators + full-width chat + session-rename fixes - #1132

Closed
zhonghuaY wants to merge 8 commits into
nesquena:masterfrom
zhonghuaY:modernize/upstream-sync
Closed

zhonghuaY wants to merge 8 commits into
nesquena:masterfrom
zhonghuaY:modernize/upstream-sync

Conversation

@zhonghuaY

Copy link
Copy Markdown

This PR contributes a coherent set of features and fixes built on top of upstream master, focused on integrating hermes-webui with an external agent-api-gateway and improving a few UX rough edges around it.

The branch is rebased onto current master (v0.50.223) — clean fast-forward, no conflicts.

Commits (8, oldest → newest)

# Commit Summary
1 feat: integrate agent-api-gateway with lowest coupling Minimal hooks so WebUI can delegate selected requests to an external agent gateway. Opt-in; behavior unchanged when no gateway is configured.
2 feat: path-based routing and /model copilot shorthand (#1) Initial routing approach + /model copilot shorthand.
3 refactor: use x-instance-keyword header instead of path-based routing (#2) Cleaner, less coupled routing — header-based dispatch supersedes #1.
4 feat: full-width chat layout + fix gateway provider test (#3) Chat area uses full width when sidebar is collapsed; matching test fix.
5 feat: status indicators — gateway dot, status-dot CSS, sidebar accent, CLI busy Real-time visual feedback: gateway health dot, per-conversation accent in sidebar, CLI busy indicator, streaming cursor.
6 fix: support renaming CLI / agent sessions in /api/session/rename Previously double-click-rename on a CLI/agent session returned 404 and the optimistic UI was reverted. Routes the rename through api.state_sync.rename_cli_session when the session is not WebUI-owned, and mirrors the new title back to state.db so /insights and "all sessions" stay in sync.
7 test(sidebar): add JS-harness tests for CLI busy + per-conversation accent Covers the indicators added in commit 5.
8 fix: restore gateway delegation + adapt #6 tests for upstream layout Keeps tests green against current upstream after the rebase.

Verification

Ran the related test files locally after rebasing onto origin/master:

pytest tests/test_gateway_provider.py tests/test_gateway_sync.py \
       tests/test_rename_session.py tests/test_sidebar_status_indicators.py \
       tests/test_streaming_gateway.py
→ 68 passed, 1 fail (environmental: connection refused — isolated server fixture
  couldn't bind in this sandbox; not a code regression)

All gateway, sidebar-status, and streaming tests pass. The one failure is the rename-after-refresh integration test which spins up an isolated server on a per-worktree port and could not bind in my local sandbox.

Notes for the reviewer

  • All gateway integration is opt-in via the x-instance-keyword header / config; with no gateway configured, request flow is identical to upstream.
  • Happy to split this into smaller PRs (gateway integration / status indicators / session-rename fix) if that's preferred for review — they were developed as separate feature branches and can be peeled apart cleanly.
  • Commits 1–3 show the routing design evolving (path → header). Squashing 1+2+3 into a single "agent-api-gateway integration" commit is also fine if you'd rather a tidier history.

jycs168 and others added 8 commits April 27, 2026 09:43
Add gateway_provider.py module for dynamic model discovery from
agent-api-gateway instances. Gateway models appear in the UI dropdown
and route through the gateway's OpenAI-compatible API with
x-instance-keyword header injection via request_overrides.

- New: api/gateway_provider.py (self-contained gateway integration)
- Modified: api/config.py (5-line delegation to gateway_provider)
- Modified: api/streaming.py (header injection via request_overrides)
- New: tests/test_gateway_provider.py (38 unit tests)
- New: tests/test_streaming_gateway.py (14 integration tests)

Made-with: Cursor
- gateway_provider: embed keyword in URL path (/v1/k/{keyword}) so no
  extra HTTP headers are needed; prefix model with 'gw:' to prevent
  Responses API auto-detection for GPT-5+ models
- commands.js: support copilot-{label}/{model}/{keyword} shorthand in
  /model command (e.g. '/model copilot-local/gpt-4.1/ins-1')

Co-authored-by: Albert Yang <yangzh0906@thundersoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…#2)

Revert to standard /v1 base URL and pass keyword via x-instance-keyword
HTTP header in extra_headers. The gw: model prefix prevents Responses
API auto-detection; the gateway strips it for strict matching.

Co-authored-by: Albert Yang <yangzh0906@thundersoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove max-width constraints from .messages-inner, .msg-body,
  .composer-box, .reconnect-banner, .update-banner, .approval-card
  so the chat area fills the available panel width
- Add 3 regression tests verifying no max-width on chat elements
- Fix test_resolve_valid_model to expect gw: model prefix

Co-authored-by: Albert Yang <yangzh0906@thundersoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, CLI busy

Re-applied #4 + nesquena#6 against upstream v0.50.217:

- Add /api/gateway-status endpoint returning instance statuses
- Add status dot CSS (.status-dot variants + .stream-cursor + accent rule)
- Add gatewayDot inside composer-model-chip (replaces removed topbar modelChip)
- Append per-session .status-dot in renderSessionListFromCache with
  data-session-id, data-cli-session, data-updated-at metadata
- Per-conversation accent via _hashHue() → --conv-accent CSS var
- Append pollGatewayStatus() (10s) + updateSessionDots() (3s) + setBusy hook
  to static/ui.js — recognizes both composerModelChip and modelChip ids
- get_gateway_instance_statuses() helper in api/gateway_provider.py

Streaming-cursor injection in messages.js was skipped because upstream
replaced the simple Thinking placeholder with smd-based incremental
rendering; upstream's session-state-indicator already covers that role.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The double-click rename action in the sidebar appeared to revert after a
page refresh for any session whose data lives in ~/.hermes/state.db
(CLI sessions, telegram/discord/gateway-imported sessions, etc.).

Root cause: the rename handler called get_session(sid) which raised
KeyError for non-WebUI-owned sessions and returned 404, while the
frontend optimistically updated the UI without awaiting the API and had
no rollback.

Fixes:

* api/state_sync.py — new rename_cli_session() that goes through
  SessionDB.set_session_title() (with sanitization + uniqueness check)
  and falls back to a defensive raw-SQL UPDATE if the on-disk schema
  version doesn't match the installed hermes_state package.
* api/routes.py — rename handler now falls through to that helper on
  KeyError, surfaces title-uniqueness conflicts as 409, and best-effort
  mirrors WebUI renames to state.db so /insights and CLI listings stay
  in sync.
* static/sessions.js — finish() now snapshots the old title and rolls
  back the optimistic update on API failure so the UI no longer lies
  about persistence.

Tests:

* tests/test_rename_session.py — 5 new tests covering WebUI rename
  persistence, length cap, blank fallback, CLI rename writing to
  state.db, and clean 404 on unknown id.
* tests/test_gateway_provider.py — 3 new tests guarding the gw: model
  prefix invariant (commit e8d89a7c), x-instance-keyword header
  presence with no path-routing leak, and cli_route 'cursor' fallback
  for unknown (model, keyword) pairs.

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

Re-applies the test file from PR nesquena#6.  The production code (status-dot
metadata, _hashHue, --conv-accent rule, updateSessionDots auto-refresh)
was folded into the earlier 'feat: status indicators' commit when the
status-dot system was re-applied against upstream v0.50.217.

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

* api/config.py: restore the 5-line gateway delegation in
  resolve_model_provider() that was dropped during the cherry-pick of
  39706bb against the heavily refactored upstream config (the cold-path
  caching layer).  Without it, test_resolve_model_provider_delegates_gateway
  fails because gateway model IDs leak through unresolved.
* tests/test_streaming_gateway.py: bump two assertions to expect the
  'gw:' model prefix introduced by 49a2be4f / e8d89a7c (already done in
  88ad007b; re-applied here since the original cherry-pick was abandoned).
* static/style.css: restate per-conversation accent rule using
  border-left-color so the regex in test_session_item_has_conv_accent_rule
  matches.
* static/sessions.js: coerce _hashHue input to string so null/undefined
  inputs don't crash (test_hash_hue_returns_int_in_range).
* static/ui.js: rewrite updateSessionDots() with a top-level
  _CLI_BUSY_WINDOW_S constant + window.HERMES_CLI_BUSY_WINDOW_S override
  so it works as a self-contained snippet under the node JS-harness;
  set dot.title='CLI active (...)' so the harness can detect promotion.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@nesquena-hermes nesquena-hermes added the enhancement New feature or request label Apr 27, 2026
@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Thanks for the contribution, @zhonghuaY! Solid writeup and a clean rebase onto master.

A few things to discuss before this can be reviewed:

Scope concern: This is a significant architectural addition — 1,971 lines across 14 files, introducing an agent-api-gateway integration as an opt-in layer. That's a meaningful surface area to review carefully.

Questions for the maintainer:

  1. Is agent-api-gateway a first-party Hermes component or a third-party service? The PR doesn't link to the gateway repo or docs — a link would help reviewers understand the trust boundary.
  2. The "full-width chat" and "session-rename fixes" commits are mixed into the same PR as the gateway integration. It would be cleaner to separate those into independent PRs — especially since fixes can ship sooner without waiting for the gateway review.
  3. What happens when no gateway is configured? The PR claims "behavior unchanged" — the test coverage for the opt-out path would be good to confirm.

Tagging for maintainer review. Holding until the gateway link/docs are added and the scope question is addressed.

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Hold — superseded by #1134

Thanks for the detailed PR, @zhonghuaY! After reviewing both #1132 and #1134, we found that #1134 is strictly better: it includes the same 8 commits plus 3 additional real bug fixes (compression-chain rename, model picker freshness, full-width CSS).

We will not merge #1132. If #1134 merges first, this PR would be a subset of already-landed work.

For the agent-api-gateway integration specifically: that's a substantial architectural addition that deserves its own dedicated issue and discussion before we consider landing it. Please open a new issue describing the use case and design — we're happy to consider it as a separate, focused PR.

nesquena-hermes added a commit that referenced this pull request Apr 30, 2026
## Release v0.50.240

Batch release of 13 PRs that passed full triage + code review + test suite (3199 tests, 0 failures).

---

### Added

- **Compact tool activity mode** (`simplified_tool_calling`, default on) — groups tool calls and thinking traces into a single collapsed "Activity" disclosure card per assistant turn. Also adds a new **Calm Console** theme with earth/slate palette and serif prose. @Michaelyklam#1282
- **PDF first-page preview** — `MEDIA:` `.pdf` files render a canvas thumbnail via PDF.js CDN (4 MB cap). **HTML sandbox iframe** — `.html`/`.htm` files render inline in a sandboxed `<iframe srcdoc>` (256 KB cap). 10 i18n keys × 7 locales. @bergeouss#1280, closes #480 #482
- **Inline Excalidraw diagram preview** — `.excalidraw` files render as pure SVG (no external deps; rectangles, ellipses, diamonds, text, lines, arrows, freehand; 512 KB cap). @bergeouss#1279, closes #479
- **Inline CSV table rendering** — fenced `csv` blocks and `MEDIA:` CSV files render as scrollable HTML tables with auto-separator detection. @bergeouss#1277, closes #485
- **Inline SVG, audio, and video rendering** — SVG as `<img>`, audio as `<audio controls>`, video as `<video controls>`. @bergeouss#1276, closes #481
- **Batch session select mode** — multi-select sessions for bulk Archive/Delete/Move. 11 i18n keys × 7 locales. @bergeouss#1275, closes #568
- **Collapsible skill category headers** — click to collapse/expand without re-render; state persists across filter cycles. @bergeouss#1281
- **`providers.only_configured` setting** — opt-in flag to restrict the model picker to explicitly configured providers. @KingBoyAndGirl — #1268
- **OpenCode Go model catalog** — adds Kimi K2.6, DeepSeek V4 Pro/Flash, MiMo V2.5/Pro, Qwen3.6/3.5 Plus. @nesquena-hermes#1284, closes #1269

### Fixed

- **Profile `TERMINAL_CWD` TypeError** — `_build_agent_thread_env()` helper merges env before `_set_thread_env()` call. @hi-friday#1266
- **Service worker subpath cache bypass** — regex now matches `/api/*` under any mount prefix. @Michaelyklam#1278
- **SSE client disconnect leaks** — `TimeoutError`/`OSError` treated as clean disconnects; server backlog 64, threads daemonized; session list renders before saved-session restore. @KayZz69#1267
- **i18n locale corrections** — Korean MCP strings (23), Chinese MCP strings (23), zh-Hant missing keys (41), de missing keys (229). @bergeouss#1274, closes #1273

---

### Test results

```
3199 passed, 2 skipped, 3 xpassed in 72.79s
```

### PRs on hold (not included)

#1265 (draft), #1271 (superseded by #1266), #1272 (skipped XSS tests), #1232 (partial test run), #1222 (review questions open), #1134 (live-server tests), #1132 (superseded by #1134), #1108 (negative UX review), #1084 (empty description)
@nesquena nesquena closed this Apr 30, 2026
SysAdminDoc pushed a commit to SysAdminDoc/hermes-webui that referenced this pull request Jun 26, 2026
## Release v0.50.240

Batch release of 13 PRs that passed full triage + code review + test suite (3199 tests, 0 failures).

---

### Added

- **Compact tool activity mode** (`simplified_tool_calling`, default on) — groups tool calls and thinking traces into a single collapsed "Activity" disclosure card per assistant turn. Also adds a new **Calm Console** theme with earth/slate palette and serif prose. @Michaelyklamnesquena#1282
- **PDF first-page preview** — `MEDIA:` `.pdf` files render a canvas thumbnail via PDF.js CDN (4 MB cap). **HTML sandbox iframe** — `.html`/`.htm` files render inline in a sandboxed `<iframe srcdoc>` (256 KB cap). 10 i18n keys × 7 locales. @bergeoussnesquena#1280, closes nesquena#480 nesquena#482
- **Inline Excalidraw diagram preview** — `.excalidraw` files render as pure SVG (no external deps; rectangles, ellipses, diamonds, text, lines, arrows, freehand; 512 KB cap). @bergeoussnesquena#1279, closes nesquena#479
- **Inline CSV table rendering** — fenced `csv` blocks and `MEDIA:` CSV files render as scrollable HTML tables with auto-separator detection. @bergeoussnesquena#1277, closes nesquena#485
- **Inline SVG, audio, and video rendering** — SVG as `<img>`, audio as `<audio controls>`, video as `<video controls>`. @bergeoussnesquena#1276, closes nesquena#481
- **Batch session select mode** — multi-select sessions for bulk Archive/Delete/Move. 11 i18n keys × 7 locales. @bergeoussnesquena#1275, closes nesquena#568
- **Collapsible skill category headers** — click to collapse/expand without re-render; state persists across filter cycles. @bergeoussnesquena#1281
- **`providers.only_configured` setting** — opt-in flag to restrict the model picker to explicitly configured providers. @KingBoyAndGirl — nesquena#1268
- **OpenCode Go model catalog** — adds Kimi K2.6, DeepSeek V4 Pro/Flash, MiMo V2.5/Pro, Qwen3.6/3.5 Plus. @nesquena-hermesnesquena#1284, closes nesquena#1269

### Fixed

- **Profile `TERMINAL_CWD` TypeError** — `_build_agent_thread_env()` helper merges env before `_set_thread_env()` call. @hi-fridaynesquena#1266
- **Service worker subpath cache bypass** — regex now matches `/api/*` under any mount prefix. @Michaelyklamnesquena#1278
- **SSE client disconnect leaks** — `TimeoutError`/`OSError` treated as clean disconnects; server backlog 64, threads daemonized; session list renders before saved-session restore. @KayZz69nesquena#1267
- **i18n locale corrections** — Korean MCP strings (23), Chinese MCP strings (23), zh-Hant missing keys (41), de missing keys (229). @bergeoussnesquena#1274, closes nesquena#1273

---

### Test results

```
3199 passed, 2 skipped, 3 xpassed in 72.79s
```

### PRs on hold (not included)

nesquena#1265 (draft), nesquena#1271 (superseded by nesquena#1266), nesquena#1272 (skipped XSS tests), nesquena#1232 (partial test run), nesquena#1222 (review questions open), nesquena#1134 (live-server tests), nesquena#1132 (superseded by nesquena#1134), nesquena#1108 (negative UX review), nesquena#1084 (empty description)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request hold

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants