Commit Graph

135 Commits

Author SHA1 Message Date
Philippe Le Rohellec 281a57b60a support slash commands implemented in hermes plugin 2026-05-11 09:42:40 -07:00
nesquena-hermes 2377216860 Stage 333: PR #2009 — feat(context): live status tracking during streaming by @dobby-d-elf 2026-05-10 18:16:59 +00:00
nesquena-hermes 83bce07d29 Stage 333: PR #2018 — fix(stop): refresh button after chat/start stream id by @rhelmer 2026-05-10 18:16:59 +00:00
nesquena-hermes fe922d83b0 Merge remote-tracking branch 'origin/master' into stage-332
# Conflicts:
#	CHANGELOG.md
2026-05-10 18:07:50 +00:00
Robert Helmer ce27499762 Fix Stop button not refreshing after chat/start stream id
Call updateSendBtn after S.activeStreamId is cleared for a new turn and
again after the server returns streamId, since setBusy(true) already
refreshed the button while activeStreamId was still null.

Add regression tests in test_1062_busy_input_modes (TestBusySendButton).
2026-05-10 10:15:16 -07:00
dobby-d-elf fecfc5f6db fix: reanchor live context usage updates 2026-05-10 10:31:14 -06:00
dobby-d-elf 1cf0ff01b5 feat: live context window status tracking during streaming 2026-05-10 06:51:46 -06:00
Frank Song 1e1a9481b4 fix(i18n): localize /goal runtime status strings 2026-05-10 15:21:24 +08:00
Michael Lam c7afae0e07 fix: lengthen auto-compression toast 2026-05-09 19:35:05 -07:00
Minimax 08c4ef8d88 feat: persistent composer draft — server-side, cross-client, survives refresh
- Session.composer_draft field: {text, files} stored in session JSON
- POST+GET /api/session/draft endpoint for save/load
- loadSession: save draft before switch, restore from S.session.composer_draft
- textarea input: debounced 400ms auto-save to server
- send(): clear draft after message is sent
- lockComposerForClarify(): save draft before card locks composer
- _restoreComposerDraft: clears textarea when target has no draft, guards
  against stale responses racing new session loads, exact text comparison
- Session.compact(): includes composer_draft in response
- Fix: use handler.command instead of parsed.method (ParseResult has no .method)

Co-authored-by: Minimax <noreply@minimax.io>
2026-05-09 13:47:57 +01:00
ai-ag2026 8f58a8c94e feat: add browser offline recovery and PWA cache hardening 2026-05-08 21:16:17 +00:00
Michael Lam 8e513b596b fix: surface goal evaluation status 2026-05-08 17:12:01 +00:00
Michael Lam 0db5bc6b76 feat: add WebUI goal command support 2026-05-08 17:12:01 +00:00
ai-ag2026 c4328c0a23 fix: keep streaming chat pinned after final render 2026-05-08 16:40:16 +00:00
Frank Song 8c02bfacd2 Restore explicit tool-segment reset calls for legacy assertions 2026-05-08 15:37:09 +00:00
Frank Song 82c7367cef Add interim_assistant streaming path to WebUI 2026-05-08 15:37:09 +00:00
Michael Lam e31b7e72d6 fix: show auto-compression running state 2026-05-07 18:41:13 +00:00
Dennis Soong fbc023bb17 fix: keep approval and clarify prompts session-owned 2026-05-07 16:58:40 +00:00
Michael Lam 2d20842450 fix: surface Codex usage exhaustion errors 2026-05-07 01:39:52 +00:00
nesquena-hermes fc5423f4aa auto-fix: preserve _setActivePaneIdleIfOwner permissive-fallback disjunct from PR #1753
PR #1753 (shipped v0.51.12) introduced the 3-way OR guard in done/error/cancel
handlers: 'isActiveSession || !S.session || !INFLIGHT[S.session.session_id]'.
The third disjunct ('no other inflight on the active pane') is the permissive
fallback Opus stage-306 verified — it allows the active pane to idle when no
other session is running, even when the completing stream is from a different
session. PR #1761's centralizing helper _setActivePaneIdleIfOwner inadvertently
dropped this disjunct, so a user viewing pane A (idle) while pane B completes
in the background would not get pane A's composer state cleared.

Restored: _setActivePaneIdleIfOwner now checks the same 3-way OR.

Verified via:
- node -c static/messages.js — clean
- pytest tests/test_session_runtime_ownership_invariants.py
       tests/test_1694_terminal_cleanup_ownership.py — 9 passed

Co-authored-by: dso2ng <dso2ng@users.noreply.github.com>
2026-05-06 22:02:37 +00:00
Dennis Soong 98a6f88ef7 fix: scope terminal stream cleanup to owner session 2026-05-07 05:56:17 +08:00
Michael Lam 1f8e8f48ac test: guard session-owned runtime invariants 2026-05-06 18:11:13 +00:00
Michael Lam ee9ae29596 fix: persist activity disclosure state 2026-05-06 06:30:32 +00:00
starship-s 74eb55d986 fix(profile): preserve context when starting chats 2026-05-06 06:27:00 +00:00
Michael Lam 2c5acb9725 feat: show active elapsed timer in compact activity 2026-05-05 13:42:47 -07:00
Michael Lam 311e69b0ba fix: preserve scroll on stream completion 2026-05-05 09:23:29 -07:00
Michael Lam c94ec31dec feat: show LLM Gateway routing metadata 2026-05-05 02:26:55 +00:00
Michael Lam e54a0470f0 Add Claude Code session imports 2026-05-05 01:18:34 +00:00
Michael Lam 3ad8846a27 fix: show TPS in assistant message headers 2026-05-04 21:26:43 +00:00
test 8a10532d29 Stage 292: PR #1601 — keep frontend routes under subpath mounts by @Michaelyklam 2026-05-04 15:34:08 +00:00
Michael Lam e9d7d5e427 fix: keep frontend routes under subpath mounts 2026-05-04 00:06:58 -07:00
Michael Lam 032b680e26 fix: render streaming markdown on subpath mounts 2026-05-03 23:55:45 -07:00
Hermes Bot d15b0a2929 Stage 290: PR #1592 — turn duration display 'Done in 1m 12s' by @Michaelyklam 2026-05-04 04:51:43 +00:00
Michael Lam 3afa23ecb7 fix: clear first-turn sidebar spinner on start failure 2026-05-03 21:14:21 -07:00
Michael Lam f3fa106cd7 feat: show agent turn duration 2026-05-03 20:20:17 -07:00
Michael Lam 9ed0639319 fix: show first-turn chats in sidebar immediately 2026-05-03 20:10:05 -07:00
Frank Song 20ef643bb8 Add messaging session handoff summary 2026-05-03 16:35:22 +00:00
Hermes Bot f0ed4aaa59 fix(sessions): sync URL after session id rotation (#1395) 2026-05-02 17:41:05 +00:00
Dennis Soong 3aafe52985 test: tighten inflight stream reuse invariants 2026-05-02 22:29:14 +08:00
Dennis Soong 6f0c5d6e1a fix: reuse inflight session stream 2026-05-02 19:12:26 +08:00
Dennis Soong 9e894a2555 fix: sync URL after session id rotation 2026-05-02 10:35:40 +08:00
nesquena-hermes 6f55b973e5 Merge PR #1390 from starship-s: preserve session provider context 2026-05-01 16:58:48 +00:00
Dennis Soong 0ec4aad949 fix: anchor active sessions per browser tab 2026-05-01 19:52:05 +08:00
starship-s 1bfc4a992a Merge branch 'nesquena:master' into fix/provider-qualified-session-models 2026-05-01 00:35:43 -06:00
starship-s bdc328d034 fix: preserve webui model provider context
Persist session model_provider separately from model IDs so active/default provider selections like gpt-5.5 remain bare while routing through OpenAI Codex. Keep @provider:model for picker disambiguation and runtime bridging, and preserve explicit OpenRouter plus custom/proxy base_url routing.
2026-04-30 23:23:47 -06:00
Hermes Agent 5c5ca7d2ef Intercept CLI-only slash commands in WebUI (#1382) 2026-05-01 04:46:30 +00:00
nesquena-hermes f53556b3ff fix(cancel-stream): rename tool_calls to _partial_tool_calls (Opus MUST-FIX)
Opus pass-2 review of v0.50.251 caught a critical regression in PR
#1375:

The cancel-partial message stored captured tool calls under the
'tool_calls' key. That key is whitelisted by _API_SAFE_MSG_KEYS so
_sanitize_messages_for_api forwarded the entries to the next-turn
LLM call. But the captured entries use the WebUI internal shape
({name, args, done, duration, is_error}) — they don't have the
OpenAI/Anthropic id + function: {name, arguments} envelope. Strict
providers (OpenAI, Anthropic, Z.AI/GLM) would 400 on the malformed
entries. Net effect: the very cancel-then-continue scenario PR
#1375 aimed to improve becomes a hard fail.

Fix:
- Rename the persisted key to '_partial_tool_calls' (underscore-
  prefixed private key NOT in _API_SAFE_MSG_KEYS, so sanitize
  correctly strips it).
- Update static/messages.js hasMessageToolMetadata check to also
  recognize _partial_tool_calls for UI rendering.
- Update test_issue1361_cancel_data_loss.py assertion to check
  _partial_tool_calls (and tool_calls as legacy fallback).

Plus 2 NIT fixes from the same Opus review:

NIT 1 (api/profiles.py:153): re.match → re.fullmatch for consistency
with other _PROFILE_ID_RE callers in the codebase. The trailing-
newline footgun ($ matches before final \n in re.match) is now
closed. Without #1373's is_dir() guard, a name like 'valid\n' would
have created a directory named 'valid\n' on Linux. Doesn't escape
<HERMES_HOME>/profiles/ via Path joining, but unintended.

NIT 2 (test_issue798.py): R19j coverage gaps — added trailing-
newline tests, length-boundary tests (64-char valid, 65-char
rejected), single-char minimum, and non-ASCII / Unicode-trick tests.

New regression test (tests/test_pr1375_partial_tool_calls_sanitize.py):
- test_partial_tool_calls_field_not_forwarded_to_llm: pins that
  sanitize-for-API strips _partial_tool_calls + reasoning + does
  NOT have tool_calls on a partial message
- test_legitimate_tool_calls_are_preserved_for_completed_turns:
  pins that real OpenAI-shape tool_calls on completed turns survive
  sanitize unchanged

Tests: 3486 passing (3484 → 3486, +2 sanitize tests).
2026-04-30 23:43:23 +00:00
nesquena-hermes 2566b434d1 fix(clarify-sse): make health timer a stale-detector, not unconditional reconnect (#1367)
Follow-up to v0.50.249 / PR #1365 absorbing Opus SHOULD-FIX #2.
Originally reset out of #1365 because the reviewer flagged it as
out-of-scope; brought back per follow-up guidance that
correctness-improving changes should ship even when out of scope.

The clarify SSE health timer at static/messages.js:1715 was an
unconditional 60s force-reconnect, not the 'no event in 60s' detector
its comment claimed. Now actually a stale-detector that tracks
lastEventAt on initial+clarify event arrivals; only reconnects when
the gap exceeds 60s. Under healthy conditions the timer never fires.

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-04-30 22:25:12 +00:00
fxd-jason d2d464aac3 feat(clarify): SSE long-connection for real-time clarify notifications (#1355)
Replaces the 1.5s HTTP polling loop for clarify with a Server-Sent Events endpoint at /api/clarify/stream that pushes clarify events to the browser instantly. Mirrors the approval SSE pattern from v0.50.248 (#1350) including all the correctness lessons:

- Atomic subscribe + initial snapshot under clarify._lock
- _clarify_sse_notify called inside _lock for ordering guarantees (no notify-out-of-order race)
- Notify passes head=q[0].data (head-fidelity, not the just-appended entry)
- resolve_clarify also calls notify after pop so trailing clarifies surface immediately (no stuck-clarify bug)
- Empty-state notify with None,0 after pop-empty so frontend hides the card
- 30s keepalive comments, _CLIENT_DISCONNECT_ERRORS handling
- Bounded queue (maxsize=16) with silent drop on full
- Frontend: EventSource with automatic 3s HTTP polling fallback on onerror

Co-authored-by: fxd-jason <wujiachen7@gmail.com>
2026-04-30 21:32:51 +00:00
nesquena-hermes bbdacdca5c fix: context window indicator overflow (#1356)
- api/streaming.py SSE payload now falls back to agent.model_metadata.get_model_context_length when compressor doesn't supply context_length (mirrors the session-save fallback shipped in v0.50.247).
- api/streaming.py also falls back to s.last_prompt_tokens to avoid using the cumulative input_tokens counter.
- static/ui.js tracks rawPct separately from pct and shows '(context exceeded)' tooltip when rawPct > 100 instead of misleading '100% used (0% left)'.
- static/messages.js clears 'Uploading...' composer status after upload completes.

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-04-30 21:32:45 +00:00