Constituent PRs: - #1760 (@ai-ag2026) preserve pending user turn on stream errors. Closes #1361. - #1761 (@dso2ng) scope terminal stream cleanup to owner session. Refs #1694. AUTO-FIX applied: restored !INFLIGHT[S.session.session_id] disjunct in _setActivePaneIdleIfOwner (regression introduced by helper centralization). - #1756 (@ng-technology-llc) isolate profile cookie per webui instance. Closes #803. - #1757 (@skspade) tri-state gateway status (alive: True/False/None). Tests: 4642 → 4662 collected (+20). 4649 passed, 9 skipped (test-isolation prong-2 noise), 3 xpassed, 0 failed in 152s. Pre-release verification: - All 4 PRs CI-green or rebased clean (#1757 had stale base; CHANGELOG conflict auto-resolved by dropping the PR's redundant entry). - node -c clean on static/messages.js + static/panels.js. - 11/11 browser API endpoints PASS. - Pre-stamp re-fetch: all PR heads match local rebases. - Opus advisor: SHIP, all 5 verification questions clean, 0 MUST-FIX, 0 SHOULD-FIX. - Two NICE-TO-HAVE coverage gaps absorbed in-release: (1) test_sprint36.py asserts !INFLIGHT[...] disjunct in helper body (2) test_issue1361_cancel_data_loss.py adds structural-grep test to pin _materialize_pending_user_turn_before_error call sites at error branches. Closes #803, #1361, #1694.
This commit is contained in:
1 parent
74edc38aac
commit
e8659d1a40
5 files changed
+82
-3
No files matched your search
@@ -1,5 +1,30 @@
|
||||
# Hermes Web UI -- Changelog
|
||||
|
||||
## [v0.51.14] — 2026-05-06 — 4-PR contributor batch
|
||||
|
||||
### Fixed
|
||||
|
||||
- **PR #1760** by @ai-ag2026 — Preserve pending user turn on stream errors. Adds reconciliation in `api/streaming.py` so the user's pending turn is appended (with timestamp + attachments) BEFORE runtime state is cleared on `apperror`-no-response and outer-Exception paths. Reload + session reconcile now see the turn instead of losing it. Includes `_materialize_pending_user_turn_before_error()` helper with dedup against eager-checkpointed messages (8-message lookback, whitespace-normalized comparison). Closes #1361.
|
||||
- **PR #1761** by @dso2ng — Scope terminal stream cleanup to owner session (refs #1694). Centralizes owner-only cleanup behind helpers (`_setActivePaneIdleIfOwner`, `_clearOwnerInflightState`, `_clearApprovalForOwner`, `_clearClarifyForOwner`) at SSE `done`/`error`/`cancel` event handlers in `static/messages.js`. Replaces inline 3-way OR guards introduced by PR #1753 (v0.51.12) with structured helper calls. The actual #1694 bug fix is in `_clearActivePaneInflightIfOwner`, which now gates `clearInflight()` on `_isActiveSession()` — previously unconditional, so a background completion would inadvertently clear the global `INFLIGHT_KEY` localStorage marker for the active pane. **Auto-fix applied**: PR's centralizing helper inadvertently dropped the `!INFLIGHT[S.session.session_id]` permissive-fallback disjunct from #1753; restored in `_setActivePaneIdleIfOwner` so the helper preserves the same 3-way OR contract Opus stage-306 verified.
|
||||
- **PR #1756** by @ng-technology-llc — Isolate profile cookie per webui instance (closes #803). Adds `WEBUI_PROFILE_COOKIE_NAME` env var so multi-instance WebUI deployments can isolate the active-profile cookie per process. Default cookie name `hermes_profile` preserved when env var not set; backwards-compatible. `get_profile_cookie_name()` resolves per-request via `os.getenv()` so deployments can change the env var without restart (existing client cookies under the old name are treated as no cookie → user re-selects profile, no data loss).
|
||||
- **PR #1757** by @skspade — Tri-state gateway status (closes earlier "gateway shows 'not running' when no platforms connected" reports). Replaces `bool(identity_map)` running signal with `agent_health.build_agent_health_payload()` as the authoritative source. Adds `alive: True/False/None` + `configured: bool` + `running: bool` fields. Frontend `static/panels.js` distinguishes three states: green "running" / amber "Gateway not configured" / red "not running". `build_agent_health_payload()` is robust to every failure (gateway import error, runtime status read exception, missing PID) — silently nulls and never raises. 247 LOC test coverage in `tests/test_gateway_status_agent_health.py`.
|
||||
|
||||
### Tests
|
||||
|
||||
4642 → **4662 collected** (+20 across 4 new test files plus regression coverage tightening). Includes 2 new structural-grep regression tests absorbed in-release per Opus advisor's NICE-TO-HAVE follow-ups: (1) `tests/test_sprint36.py` now asserts `_setActivePaneIdleIfOwner` body contains the `!INFLIGHT[...]` disjunct (catches the auto-fix repaired regression in #1761); (2) `tests/test_issue1361_cancel_data_loss.py` adds `test_materialize_helper_called_immediately_before_error_path_clears` to pin the helper call's call-site location in `api/streaming.py` error branches (catches future refactor that drops the call but keeps the clearing).
|
||||
|
||||
### Pre-release verification
|
||||
|
||||
- All 4 PRs CI-green individually (#1760, #1761) or rebased clean (#1756, #1757 — #1757 had stale base from before v0.51.10 stamps; CHANGELOG conflict auto-resolved by dropping the PR's redundant changelog entry, since we write the v0.51.14 entry at stamp time).
|
||||
- Auto-fix on #1761 verified by 9-test pass before merge (5 invariants + 4 new ownership tests).
|
||||
- `node -c` clean on both `static/messages.js` and `static/panels.js`.
|
||||
- pytest: 4649 passed, 0 failed (single clean run, ~152s).
|
||||
- `scripts/run-browser-tests.sh`: all 11 endpoints PASS on isolated port 8789.
|
||||
- Pre-stamp re-fetch: all 4 PR heads still match local rebases — no late commits.
|
||||
- Opus advisor: SHIP all 4, all 5 verification questions clean, 0 MUST-FIX, 0 SHOULD-FIX. Two NICE-TO-HAVE coverage gaps absorbed in-release as ~30 LOC of defensive structural-grep regression tests (covered above).
|
||||
|
||||
Closes #803, #1361, #1694.
|
||||
|
||||
## [v0.51.13] — 2026-05-06 — single-PR composer UX
|
||||
|
||||
### Added
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
> Web companion to the Hermes Agent CLI. Same workflows, browser-native.
|
||||
>
|
||||
> Last updated: v0.51.13 (May 6, 2026) — 4642 tests collected — single-PR composer UX (#1758)
|
||||
> Last updated: v0.51.14 (May 6, 2026) — 4662 tests collected — 4-PR contributor batch (#1756, #1757, #1760, #1761)
|
||||
> Test source: `pytest tests/ --collect-only -q`
|
||||
> Per-version detail: see [CHANGELOG.md](./CHANGELOG.md)
|
||||
|
||||
|
||||
+2
-2
@@ -1835,8 +1835,8 @@ Bridged CLI sessions:
|
||||
|
||||
---
|
||||
|
||||
*Last updated: v0.51.13, May 6, 2026*
|
||||
*Total automated tests collected: 4642*
|
||||
*Last updated: v0.51.14, May 6, 2026*
|
||||
*Total automated tests collected: 4662*
|
||||
*Regression gate: tests/test_regressions.py*
|
||||
*Run: pytest tests/ -v --timeout=60*
|
||||
*Source: <repo>/*
|
||||
@@ -364,3 +364,46 @@ def test_stream_error_pending_materialization_does_not_duplicate_eager_checkpoin
|
||||
|
||||
assert appended is False
|
||||
assert [m.get("role") for m in s.messages].count("user") == 1
|
||||
|
||||
|
||||
# ── Structural guard: pin call sites of the materialize helper at error branches ──
|
||||
|
||||
def test_materialize_helper_called_immediately_before_error_path_clears():
|
||||
"""Pin call sites of _materialize_pending_user_turn_before_error.
|
||||
|
||||
Catches a future refactor that drops the call from the apperror-no-response
|
||||
or outer-Exception paths in api/streaming.py while leaving the
|
||||
`pending_user_message = None` clearing in place — which is exactly the
|
||||
user-turn-data-loss regression #1361 was filed for.
|
||||
|
||||
Strategy: count how many `pending_user_message = None` clearings have the
|
||||
helper call within the preceding 4 lines. Currently 2 (apperror at 2610,
|
||||
outer-Exception at 3072). The success path (2716) and cancel path (3375)
|
||||
legitimately don't need the helper. If a future refactor drops the helper
|
||||
call from one of the error sites, this assertion fires.
|
||||
"""
|
||||
from pathlib import Path
|
||||
src = Path(__file__).parent.parent.joinpath('api', 'streaming.py').read_text(encoding='utf-8')
|
||||
lines = src.splitlines()
|
||||
|
||||
helper_name = '_materialize_pending_user_turn_before_error('
|
||||
clear_sites = [(i + 1, line) for i, line in enumerate(lines)
|
||||
if 'pending_user_message = None' in line]
|
||||
assert len(clear_sites) >= 4, (
|
||||
f"Expected ≥4 sites that clear pending_user_message; found {len(clear_sites)}. "
|
||||
f"If api/streaming.py was refactored, re-audit this test."
|
||||
)
|
||||
|
||||
sites_with_helper = []
|
||||
for lineno, _ in clear_sites:
|
||||
prev_block = '\n'.join(lines[max(0, lineno - 5):lineno - 1])
|
||||
if helper_name in prev_block:
|
||||
sites_with_helper.append(lineno)
|
||||
|
||||
# Concretely, PR #1760 wired up the helper at the apperror-no-response
|
||||
# path and the outer-Exception path. Both must remain wired.
|
||||
assert len(sites_with_helper) >= 2, (
|
||||
f"Expected ≥2 clear sites preceded by {helper_name} within 4 lines; "
|
||||
f"found {sites_with_helper}. PR #1760 / #1361 regression — re-wire the "
|
||||
f"helper at the error-branch clear sites in api/streaming.py."
|
||||
)
|
||||
@@ -178,6 +178,17 @@ def test_sse_cancel_handler_calls_set_busy():
|
||||
next_function = src.find("\n function ", helper_idx + 1)
|
||||
helper = src[helper_idx:next_function if next_function != -1 else helper_idx + 800]
|
||||
assert "setBusy(false)" in helper
|
||||
# The helper MUST preserve the v0.51.12 (#1753) 3-way OR guard so
|
||||
# idling the active pane on a background completion is gated on the
|
||||
# permissive-fallback disjunct ("no other inflight on the active pane")
|
||||
# in addition to "is active" / "no session". Without this, a user
|
||||
# viewing pane A (idle) while pane B completes in the background
|
||||
# would not get pane A's composer state cleared. Catches the exact
|
||||
# regression v0.51.14's auto-fix repaired in PR #1761.
|
||||
assert "!INFLIGHT[S.session.session_id]" in helper, (
|
||||
"_setActivePaneIdleIfOwner must preserve the !INFLIGHT[...] "
|
||||
"permissive-fallback disjunct from PR #1753 (v0.51.12)."
|
||||
)
|
||||
|
||||
|
||||
# ── 7. i18n key preserved ─────────────────────────────────────────────────────
|
||||
|
||||
Reference in new issue
Block a user