HERMES-GIELIE/api
nesquena-hermes bea57beba9 fix(streaming): SSE heartbeat alignment, repair grace period, local-server model id preservation (#1623, #1624, #1625)
Closes #1623 — Lower SSE app heartbeat from 30s to 5s at every long-lived
handler (main agent, terminal, gateway-watcher, approval-poller, clarify-poller).
Kernel TCP keepalive declares peer dead at 25s worst-case (10s KEEPIDLE +
5s KEEPINTVL * 3 KEEPCNT, added v0.50.289 #1581). 30s app heartbeat let the
kernel tear sockets down on flaky networks before the app sent its first
keepalive byte — drops at ~10s during long thinking phases. New named
constant _SSE_HEARTBEAT_INTERVAL_SECONDS=5; regression test pins the
inequality (app_heartbeat * 2 <= kernel_window) so future tuning can't
re-introduce the misalignment.

Closes #1624 — Add 30s grace period to _repair_stale_pending() trigger.
Without it, any narrow race between the streaming thread clearing
pending_user_message and STREAMS.pop(stream_id) produces a false-positive
'Previous turn did not complete.' marker on a turn that finished correctly
(reproducible after every command-approval turn). Defense-in-depth, not
the root-cause fix — the actual streaming-thread leak path is tracked
separately. Falsy pending_started_at (legacy sidecars) treated as
'old enough' so legitimate legacy-data recovery still works. Plus
logger.warning telemetry on every legitimate repair so the next batch of
user reports tells us whether the underlying race still fires.

Closes #1625 — Local model servers (LM Studio, Ollama, llama.cpp, vLLM,
TabbyAPI, koboldcpp, textgen-webui) now keep the full HuggingFace-style
model id (e.g. 'qwen/qwen3.6-27b' instead of stripped 'qwen3.6-27b'). New
_LOCAL_SERVER_PROVIDERS set + _base_url_points_at_local_server() loopback/
RFC1918 heuristic — either signal triggers no-strip. Backward compat
preserved for OpenAI-compatible proxies on public hosts (LiteLLM at
litellm.example.com still strips openai/gpt-5.4 -> gpt-5.4). Updated the
existing #230/#433 test to reflect that #1625 supersedes the strip-on-custom
rule for loopback hosts (see api/config.py and test_model_resolver.py
docstring update). Reported by @akarichan8231 in Discord on 2026-05-04.

42 regression tests across:
  tests/test_issue1623_sse_heartbeat_alignment.py (3)
  tests/test_issue1624_repair_stale_pending_grace.py (9)
  tests/test_issue1625_local_server_model_id_preservation.py (30)

4142 -> 4184 passing. 0 regressions.
2026-05-04 16:49:43 +00:00
..
__init__.py Hermes Web UI — Sprints 11-14: multi-provider models, settings, session QoL, alerts, polish 2026-03-31 07:02:47 +00:00
agent_sessions.py fix: keep cross-source continuations separate in sidebar 2026-05-04 09:30:47 +02:00
auth.py fix: keep frontend routes under subpath mounts 2026-05-04 00:06:58 -07:00
background.py feat(commands): /background, /btw slash commands + undo button + reasoning chip 2026-04-24 01:24:51 +00:00
clarify.py feat(clarify): SSE long-connection for real-time clarify notifications (#1355) 2026-04-30 21:32:51 +00:00
commands.py feat: slash command parity + skill autocomplete — v0.50.91 (PR #711) 2026-04-19 05:37:44 +00:00
config.py fix(streaming): SSE heartbeat alignment, repair grace period, local-server model id preservation (#1623, #1624, #1625) 2026-05-04 16:49:43 +00:00
extensions.py Opus pre-release follow-ups for PR #1445 2026-05-02 03:49:40 +00:00
gateway_watcher.py release: v0.50.241 (#1293) 2026-04-29 19:54:07 -07:00
helpers.py v0.50.255: Opus follow-ups (4 fixes) + CHANGELOG 2026-05-01 17:19:53 +00:00
metering.py v0.50.207: batch of 10 PRs — TPS stat, SSE guard, session polish, cron UX, folder create, model errors, session speed, title gen (#1031) 2026-04-25 13:07:35 -07:00
models.py fix(streaming): SSE heartbeat alignment, repair grace period, local-server model id preservation (#1623, #1624, #1625) 2026-05-04 16:49:43 +00:00
oauth.py feat: silent credential self-heal on 401 errors (#1401) 2026-05-03 18:32:53 +00:00
onboarding.py fix(onboarding,probe): refuse HTTP redirects on probe path (reviewer-flagged on PR #1501) 2026-05-03 03:21:22 +00:00
profiles.py Apply Opus pre-release SHOULD-FIX + NITs (in-PR per release policy) 2026-05-04 16:17:26 +00:00
providers.py test: cover provider config cleanup path 2026-05-04 09:04:07 -07:00
rollback.py v0.50.255: Opus follow-ups (4 fixes) + CHANGELOG 2026-05-01 17:19:53 +00:00
routes.py fix(streaming): SSE heartbeat alignment, repair grace period, local-server model id preservation (#1623, #1624, #1625) 2026-05-04 16:49:43 +00:00
session_ops.py Show profile home in /status command (refs #463) (#1380) 2026-05-01 04:46:37 +00:00
session_recovery.py fix: absorb Opus advisor doc-only SHOULD-FIX nits 2026-05-03 20:54:02 +00:00
startup.py fix: P0 hotfixes — API regression, code block parser, chmod override 2026-05-01 12:10:48 +00:00
state_sync.py security: bandit fixes B310/B324/B110 + QuietHTTPServer (#354) 2026-04-13 11:11:56 -07:00
streaming.py fix(streaming): use truthy-check for _pending_started_at fallback 2026-05-03 23:21:19 -07:00
terminal.py absorb: address Opus review findings (security + correctness) 2026-04-29 05:06:34 +00:00
updates.py chore(release): stamp v0.50.293 — 3-PR batch + 2 Opus follow-ups absorbed 2026-05-04 16:33:57 +00:00
upload.py Merge remote-tracking branch 'refs/remotes/pr/1229' into stage/batch-v0.50.238 2026-04-29 15:17:57 +00:00
workspace.py fix: batch v0.50.234-235 — XSS hardening, workspace validation, profile switch fixes (#1206) 2026-04-27 21:39:30 -07:00