Extend WebSocket heartbeat grace

Raises the Socket.IO heartbeat interval and timeout defaults so long context and prompt work do not trip python-engineio's empty packet queue timeout. Adds positive-integer environment overrides and updates the runtime configuration regression test.

Alessandro committed Jun 16, 2026 at 16:29 UTC 1dc719ff9c107beee4965b6946143e4b07b71f81
3 files changed +40 -8
helpers/ui_server.py
+21 -2
@@ -37,6 +37,19 @@ from helpers.ws_manager import WsManager, set_shared_ws_manager
37
38
39 UPLOAD_LIMIT_BYTES = 5 * 1024 * 1024 * 1024
40 +SOCKETIO_PING_INTERVAL_SECONDS = 45
41 +SOCKETIO_PING_TIMEOUT_SECONDS = 120
42 +
43 +
44 +def _positive_int_env(name: str, default: int) -> int:
45 + raw_value = os.getenv(name)
46 + if raw_value is None:
47 + return default
48 + try:
49 + value = int(raw_value)
50 + except (TypeError, ValueError):
51 + return default
52 + return value if value > 0 else default
53
54
55 def configure_process_environment() -> None:
@@ -85,8 +98,14 @@ class UiServerRuntime:
98 cors_allowed_origins=lambda _origin, environ: validate_ws_origin(environ)[0],
99 logger=False,
100 engineio_logger=False,
88 - ping_interval=25,
89 - ping_timeout=20,
101 + ping_interval=_positive_int_env(
102 + "A0_SOCKETIO_PING_INTERVAL_SECONDS",
103 + SOCKETIO_PING_INTERVAL_SECONDS,
104 + ),
105 + ping_timeout=_positive_int_env(
106 + "A0_SOCKETIO_PING_TIMEOUT_SECONDS",
107 + SOCKETIO_PING_TIMEOUT_SECONDS,
108 + ),
109 max_http_buffer_size=50 * 1024 * 1024,
110 )
111
helpers/ui_server.py.dox.md
+4 -2
@@ -26,19 +26,21 @@
26 - `async serve_plugin_asset(self, plugin_name, asset_path)`
27 - `async serve_extension_asset(self, asset_path)`
28 - Top-level functions:
29 +- `_positive_int_env(name: str, default: int) -> int`
30 - `configure_process_environment() -> None`
30 -- Notable constants/configuration names: `UPLOAD_LIMIT_BYTES`.
31 +- Notable constants/configuration names: `UPLOAD_LIMIT_BYTES`, `SOCKETIO_PING_INTERVAL_SECONDS`, `SOCKETIO_PING_TIMEOUT_SECONDS`, `A0_SOCKETIO_PING_INTERVAL_SECONDS`, `A0_SOCKETIO_PING_TIMEOUT_SECONDS`.
32
33 ## Runtime Contracts
34
35 - Helper modules own reusable framework APIs and must preserve public callers unless all callers, tests, and docs are updated together.
36 - Update this file whenever public functions, classes, persistence behavior, path/security assumptions, side effects, or cross-module contracts change.
37 +- Socket.IO heartbeat defaults are intentionally longer than Engine.IO's short defaults so CLI sessions survive long prompt/context work; environment overrides must remain positive integers and fall back to source defaults when invalid.
38 - Observed side-effect areas: filesystem reads, network calls, subprocess/runtime control, WebSocket state, plugin state, settings/state persistence, secret handling.
39 - Imported dependency areas include: `asyncio`, `dataclasses`, `datetime`, `flask`, `helpers`, `helpers.api`, `helpers.extension`, `helpers.files`, `helpers.print_style`, `helpers.server_startup`, `helpers.ws`, `helpers.ws_manager`, `logging`, `os`, `secrets`, `socketio`.
40
41 ## Key Concepts
42
41 -- Important called helpers/classes observed in the source: `logging.getLogger.setLevel`, `Localization.get.apply_process_timezone`, `field`, `Flask`, `threading.RLock`, `socketio.AsyncServer`, `WsManager`, `set_shared_ws_manager`, `cls`, `server_runtime.refresh_runtime_settings`, `settings_helper.get_settings`, `settings_helper.set_runtime_settings_snapshot`, `self.ws_manager.set_server_restart_broadcast`, `UiRouteHandlers`, `self.webapp.add_url_rule`, `register_api_route`, `register_ws_namespace`, `files.read_file`, `render_template_string`, `session.pop`.
43 +- Important called helpers/classes observed in the source: `logging.getLogger.setLevel`, `Localization.get.apply_process_timezone`, `_positive_int_env`, `field`, `Flask`, `threading.RLock`, `socketio.AsyncServer`, `WsManager`, `set_shared_ws_manager`, `cls`, `server_runtime.refresh_runtime_settings`, `settings_helper.get_settings`, `settings_helper.set_runtime_settings_snapshot`, `self.ws_manager.set_server_restart_broadcast`, `UiRouteHandlers`, `self.webapp.add_url_rule`, `register_api_route`, `register_ws_namespace`, `files.read_file`, `render_template_string`, `session.pop`.
44 - Keep request/response, tool, or helper semantics documented here at the same time as source changes.
45
46 ## Work Guidance
tests/test_run_ui_config.py
+15 -4
@@ -5,12 +5,23 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
5 if str(PROJECT_ROOT) not in sys.path:
6 sys.path.insert(0, str(PROJECT_ROOT))
7
8 -import run_ui
8 +from helpers.ui_server import UiServerRuntime
9
10
11 def test_socketio_engine_configuration_defaults():
12 - server = run_ui.socketio_server.eio
12 + server = UiServerRuntime.create().socketio_server.eio
13
14 - assert server.ping_interval == 25
15 - assert server.ping_timeout == 20
14 + assert server.ping_interval == 45
15 + assert server.ping_timeout == 120
16 + assert server.max_http_buffer_size == 50 * 1024 * 1024
17 +
18 +
19 +def test_socketio_engine_configuration_uses_env_overrides(monkeypatch):
20 + monkeypatch.setenv("A0_SOCKETIO_PING_INTERVAL_SECONDS", "30")
21 + monkeypatch.setenv("A0_SOCKETIO_PING_TIMEOUT_SECONDS", "90")
22 +
23 + server = UiServerRuntime.create().socketio_server.eio
24 +
25 + assert server.ping_interval == 30
26 + assert server.ping_timeout == 90
27 assert server.max_http_buffer_size == 50 * 1024 * 1024