feat: real-time gateway session sync (Phase 1) - #274
Conversation
- Add gateway_watcher.py: background daemon polling state.db every 5s for gateway session changes (telegram, discord, slack, etc.) - Extend get_cli_sessions() to include all non-webui sources - Add SSE endpoint /api/sessions/gateway/stream for real-time push - Add dynamic source badges (telegram=blue, discord=purple, slack=dark purple) - Rename 'Show CLI sessions' to 'Show agent sessions' - Wire watcher lifecycle into server start/stop - 10 tests covering metadata, filtering, SSE, and watcher lifecycle - Activated via the same checkbox as CLI session import Addresses GitHub issue nesquena#272
- Fix critical SSE bug: frontend listened for 'gateway_session_update' but backend sends 'sessions_changed' -- events were silently dropped - Fix frontend field check: data.changed -> data.sessions (matches the actual payload structure from gateway_watcher) - Fix TLS: ssl.TLSv1_2 -> ssl.TLSVersion.TLSv1_2 (the bare attribute does not exist, would crash TLS setup and silently fall back to HTTP) - Remove PLAN.md: implementation plan should not be committed to repo Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Full Review: PR #274 — real-time gateway session syncThanks @bergeouss! Solid architecture — polling state.db with hash-based change detection + SSE push is the right approach for real-time sync without modifying hermes-agent. Security AuditClean. Bugs Found and Fixed (pushed to branch)1. Critical: SSE event name mismatch 2. Frontend field check wrong 3. TLS attribute broken 4. PLAN.md removed Code Review
Frontend (
Tests — Comprehensive (10 tests): Notes
Tests604 passed, 0 regressions (1 pre-existing failure from unmerged PR #243). VerdictApproved with the 4 fixes pushed. The architecture is sound, the feature works correctly now that the SSE event names match, and the tests are comprehensive. |
|
Merged via PR #279 which cherry-picks both commits onto current master (avoiding a stale-base regression that would have deleted the Spanish locale). Three additional fixes on top of the 4 already applied:
658/658 tests pass. Feature verified in browser (Telegram/Discord/Slack badges render correctly, SSE endpoint live). Thanks @bergeouss! |
* feat: add real-time gateway session sync (Phase 1) - Add gateway_watcher.py: background daemon polling state.db every 5s for gateway session changes (telegram, discord, slack, etc.) - Extend get_cli_sessions() to include all non-webui sources - Add SSE endpoint /api/sessions/gateway/stream for real-time push - Add dynamic source badges (telegram=blue, discord=purple, slack=dark purple) - Rename 'Show CLI sessions' to 'Show agent sessions' - Wire watcher lifecycle into server start/stop - 10 tests covering metadata, filtering, SSE, and watcher lifecycle - Activated via the same checkbox as CLI session import Addresses GitHub issue #272 * fix: SSE event name mismatch, TLS attribute, remove PLAN.md - Fix critical SSE bug: frontend listened for 'gateway_session_update' but backend sends 'sessions_changed' -- events were silently dropped - Fix frontend field check: data.changed -> data.sessions (matches the actual payload structure from gateway_watcher) - Fix TLS: ssl.TLSv1_2 -> ssl.TLSVersion.TLSv1_2 (the bare attribute does not exist, would crash TLS setup and silently fall back to HTTP) - Remove PLAN.md: implementation plan should not be committed to repo Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: test isolation and slow-consumer sentinel in gateway sync tests/test_gateway_sync.py: - Fix _get_test_state_dir() path mismatch: the function was computing HERMES_HOME/webui-mvp-test but conftest.py sets HERMES_HOME=TEST_STATE_DIR, so state.db was written to a double-nested path the server never read. Now uses HERMES_WEBUI_STATE_DIR first (which conftest sets directly to TEST_STATE_DIR), fixing the 7/10 test failures in full-suite ordering. - Fix conn cleanup: removed conn.close() from inside try blocks so the connection stays valid for _remove_test_sessions() in the finally block. Previously the closed conn caused ProgrammingError in finally (swallowed by bare except), leaving ghost sessions in state.db on test failure. api/gateway_watcher.py: - Fix slow-consumer queue eviction: when a subscriber queue fills (>10 events) and is removed from _subscribers, now puts a None sentinel into it so the SSE handler unblocks and closes the connection, letting EventSource auto-reconnect. Without this the connection stayed open but received no further events. * fix: test isolation — set HERMES_WEBUI_TEST_STATE_DIR in conftest The gateway sync tests write directly to state.db and must use the same path the test server reads from. Previously they computed the path independently, which broke when test_auth_sessions.py set a different HERMES_WEBUI_STATE_DIR in the test-process environment at import time. tests/conftest.py: - Set HERMES_WEBUI_TEST_STATE_DIR=TEST_STATE_DIR in the test process's os.environ (via setdefault) so gateway tests can read it reliably. Using setdefault preserves any explicit override the caller may pass. tests/test_gateway_sync.py: - Simplify _get_test_state_dir(): check HERMES_WEBUI_TEST_STATE_DIR first (now reliably set by conftest), fall back to HERMES_HOME/webui-mvp-test. Remove the workaround that tried to snapshot HERMES_HOME at import time. Result: 658/658 tests pass in full-suite ordering (was 651 pass / 7 fail). --------- Co-authored-by: bergeouss <bergeouss@users.noreply.github.com> Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@bergeouss Can you confirm that in the latest master this is working as expected as anticipated? |
|
Confirmed! Running the latest |
|
@bergeouss Interested in being a core contributor to this WebUI project? Reach out to me via email (on my profile) or on Twitter if so! |
* feat: add real-time gateway session sync (Phase 1) - Add gateway_watcher.py: background daemon polling state.db every 5s for gateway session changes (telegram, discord, slack, etc.) - Extend get_cli_sessions() to include all non-webui sources - Add SSE endpoint /api/sessions/gateway/stream for real-time push - Add dynamic source badges (telegram=blue, discord=purple, slack=dark purple) - Rename 'Show CLI sessions' to 'Show agent sessions' - Wire watcher lifecycle into server start/stop - 10 tests covering metadata, filtering, SSE, and watcher lifecycle - Activated via the same checkbox as CLI session import Addresses GitHub issue nesquena#272 * fix: SSE event name mismatch, TLS attribute, remove PLAN.md - Fix critical SSE bug: frontend listened for 'gateway_session_update' but backend sends 'sessions_changed' -- events were silently dropped - Fix frontend field check: data.changed -> data.sessions (matches the actual payload structure from gateway_watcher) - Fix TLS: ssl.TLSv1_2 -> ssl.TLSVersion.TLSv1_2 (the bare attribute does not exist, would crash TLS setup and silently fall back to HTTP) - Remove PLAN.md: implementation plan should not be committed to repo Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: test isolation and slow-consumer sentinel in gateway sync tests/test_gateway_sync.py: - Fix _get_test_state_dir() path mismatch: the function was computing HERMES_HOME/webui-mvp-test but conftest.py sets HERMES_HOME=TEST_STATE_DIR, so state.db was written to a double-nested path the server never read. Now uses HERMES_WEBUI_STATE_DIR first (which conftest sets directly to TEST_STATE_DIR), fixing the 7/10 test failures in full-suite ordering. - Fix conn cleanup: removed conn.close() from inside try blocks so the connection stays valid for _remove_test_sessions() in the finally block. Previously the closed conn caused ProgrammingError in finally (swallowed by bare except), leaving ghost sessions in state.db on test failure. api/gateway_watcher.py: - Fix slow-consumer queue eviction: when a subscriber queue fills (>10 events) and is removed from _subscribers, now puts a None sentinel into it so the SSE handler unblocks and closes the connection, letting EventSource auto-reconnect. Without this the connection stayed open but received no further events. * fix: test isolation — set HERMES_WEBUI_TEST_STATE_DIR in conftest The gateway sync tests write directly to state.db and must use the same path the test server reads from. Previously they computed the path independently, which broke when test_auth_sessions.py set a different HERMES_WEBUI_STATE_DIR in the test-process environment at import time. tests/conftest.py: - Set HERMES_WEBUI_TEST_STATE_DIR=TEST_STATE_DIR in the test process's os.environ (via setdefault) so gateway tests can read it reliably. Using setdefault preserves any explicit override the caller may pass. tests/test_gateway_sync.py: - Simplify _get_test_state_dir(): check HERMES_WEBUI_TEST_STATE_DIR first (now reliably set by conftest), fall back to HERMES_HOME/webui-mvp-test. Remove the workaround that tried to snapshot HERMES_HOME at import time. Result: 658/658 tests pass in full-suite ordering (was 651 pass / 7 fail). --------- Co-authored-by: bergeouss <bergeouss@users.noreply.github.com> Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add real-time gateway session sync (Phase 1) - Add gateway_watcher.py: background daemon polling state.db every 5s for gateway session changes (telegram, discord, slack, etc.) - Extend get_cli_sessions() to include all non-webui sources - Add SSE endpoint /api/sessions/gateway/stream for real-time push - Add dynamic source badges (telegram=blue, discord=purple, slack=dark purple) - Rename 'Show CLI sessions' to 'Show agent sessions' - Wire watcher lifecycle into server start/stop - 10 tests covering metadata, filtering, SSE, and watcher lifecycle - Activated via the same checkbox as CLI session import Addresses GitHub issue nesquena#272 * fix: SSE event name mismatch, TLS attribute, remove PLAN.md - Fix critical SSE bug: frontend listened for 'gateway_session_update' but backend sends 'sessions_changed' -- events were silently dropped - Fix frontend field check: data.changed -> data.sessions (matches the actual payload structure from gateway_watcher) - Fix TLS: ssl.TLSv1_2 -> ssl.TLSVersion.TLSv1_2 (the bare attribute does not exist, would crash TLS setup and silently fall back to HTTP) - Remove PLAN.md: implementation plan should not be committed to repo * fix: test isolation and slow-consumer sentinel in gateway sync tests/test_gateway_sync.py: - Fix _get_test_state_dir() path mismatch: the function was computing HERMES_HOME/webui-mvp-test but conftest.py sets HERMES_HOME=TEST_STATE_DIR, so state.db was written to a double-nested path the server never read. Now uses HERMES_WEBUI_STATE_DIR first (which conftest sets directly to TEST_STATE_DIR), fixing the 7/10 test failures in full-suite ordering. - Fix conn cleanup: removed conn.close() from inside try blocks so the connection stays valid for _remove_test_sessions() in the finally block. Previously the closed conn caused ProgrammingError in finally (swallowed by bare except), leaving ghost sessions in state.db on test failure. api/gateway_watcher.py: - Fix slow-consumer queue eviction: when a subscriber queue fills (>10 events) and is removed from _subscribers, now puts a None sentinel into it so the SSE handler unblocks and closes the connection, letting EventSource auto-reconnect. Without this the connection stayed open but received no further events. * fix: test isolation — set HERMES_WEBUI_TEST_STATE_DIR in conftest The gateway sync tests write directly to state.db and must use the same path the test server reads from. Previously they computed the path independently, which broke when test_auth_sessions.py set a different HERMES_WEBUI_STATE_DIR in the test-process environment at import time. tests/conftest.py: - Set HERMES_WEBUI_TEST_STATE_DIR=TEST_STATE_DIR in the test process's os.environ (via setdefault) so gateway tests can read it reliably. Using setdefault preserves any explicit override the caller may pass. tests/test_gateway_sync.py: - Simplify _get_test_state_dir(): check HERMES_WEBUI_TEST_STATE_DIR first (now reliably set by conftest), fall back to HERMES_HOME/webui-mvp-test. Remove the workaround that tried to snapshot HERMES_HOME at import time. Result: 658/658 tests pass in full-suite ordering (was 651 pass / 7 fail). --------- Co-authored-by: bergeouss <bergeouss@users.noreply.github.com> Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
Summary
Implements Phase 1 from #272 — real-time bidirectional sync between Gateway sessions and WebUI.
What this does
When the "Show agent sessions" checkbox is enabled (previously "Show CLI sessions"), gateway sessions from Telegram, Discord, Slack, and other messaging platforms now appear in the sidebar and update in real-time as new messages arrive.
Backend
state.dbevery 5 seconds for gateway session changes, using MD5 hash of session IDs + timestamps for efficient change detection/api/sessions/gateway/streamfor pushing detected changes to the browserget_cli_sessions()to include all non-webui sources (telegram, discord, slack, etc.) with proper source taggingFrontend
Tests
tests/test_gateway_sync.pycovering metadata, filtering, SSE endpoint, and watcher lifecycleArchitecture
Zero changes to hermes-agent — the WebUI reads the shared
state.dbthat both components already access.Addresses
Closes #272