Skip to content

feat: Add WebSocket heartbeat configuration support with enhanced logging - #51

Merged
Leoyzen merged 5 commits into
develop/agenticfrom
feat/websocket-heartbeat-config
Jun 22, 2026
Merged

feat: Add WebSocket heartbeat configuration support with enhanced logging#51
Leoyzen merged 5 commits into
develop/agenticfrom
feat/websocket-heartbeat-config

Conversation

@Leoyzen

@Leoyzen Leoyzen commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add configurable WebSocket heartbeat support with enhanced logging and MCP timeout improvements.

Changes

  • feat: Add WebSocket heartbeat configuration support — Enable configurable heartbeat intervals for WebSocket connections
  • feat: Enhance WebSocket heartbeat logging with detailed info and debug messages — Improve observability of heartbeat mechanism
  • fix(acp): increase timeout for mcp/connect and mcp/message to 300 seconds — Prevent timeout issues with slow MCP connections
  • fix(acp): increase default read timeout for MCP transport and session handling — Increase default read timeout to 600s for MCP transport

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces configurable WebSocket heartbeat settings (ping interval, pong timeout, and max missed pongs) across transports, CLI commands, and server configurations, alongside a custom heartbeat monitoring task. It also significantly increases several read and connection timeouts. Feedback on these changes suggests wrapping cleanup operations in finally blocks with try-except to avoid masking exceptions, addressing a potential timing drift in the custom heartbeat loop, and reconsidering the excessively long 5-minute and 10-minute timeouts introduced for MCP transport and session initialization.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/acp/transports.py Outdated
Comment thread src/acp/transports.py
Comment on lines +423 to +430
while True:
await asyncio.sleep(ping_interval)
ping_count += 1
logger.debug("Sending WebSocket ping #%d", ping_count)
try:
pong_waiter: Awaitable[float] = await websocket.ping()
await asyncio.wait_for(pong_waiter, timeout=pong_timeout)
logger.debug("Pong #%d received in time", ping_count)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current sequential implementation of _websocket_heartbeat introduces a drift in the ping interval. Because await asyncio.wait_for(pong_waiter, timeout=pong_timeout) is awaited sequentially after await asyncio.sleep(ping_interval), the actual time between pings becomes ping_interval + latency (or ping_interval + pong_timeout on timeout). This causes the heartbeat to drift and behave inconsistently compared to standard heartbeats (like Uvicorn's, which sends pings at fixed intervals). This also creates a discrepancy between WebSocketTransport (which takes 270s to close on 3 missed pongs) and ACPWebSocketTransport (which takes 210s to close). Consider scheduling the ping/pong checks concurrently or adjusting the sleep interval to maintain a consistent ping interval.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional. The custom heartbeat is designed to be "forgiving" — it waits for the pong response before sending the next ping, only closing after consecutive missed pongs. This differs from the standard websockets library's fixed-interval approach. The drift is a byproduct of this design choice, not a bug. If precise interval timing becomes necessary in the future, we can switch to absolute-time scheduling.

Comment thread src/agentpool_server/acp_server/acp_mcp_transport.py
Comment thread src/agentpool_server/acp_server/session.py Outdated
A-Qiang added 4 commits June 22, 2026 10:48
- Add heartbeat parameters (ping_interval, pong_timeout, max_missed_pongs) to transport classes
- Implement _websocket_heartbeat function for proactive connection monitoring
- Update CLI with --ws-ping-interval, --ws-pong-timeout, --ws-max-missed-pongs flags
- Add ACPPoolServerConfig heartbeat fields for YAML configuration
- Integrate heartbeat logic into _serve_websocket and _serve_streamable_http
- Support disabling heartbeat via ping_interval=None

Defaults: ping_interval=60s, pong_timeout=30s, max_missed_pongs=3
@Leoyzen
Leoyzen force-pushed the feat/websocket-heartbeat-config branch from 7fb9d85 to 8e00feb Compare June 22, 2026 02:48
@Leoyzen Leoyzen changed the title Feat/websocket heartbeat config feat: Add WebSocket heartbeat configuration support with enhanced logging Jun 22, 2026
…duce MCP transport fallback timeout

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@Leoyzen
Leoyzen merged commit 1557dda into develop/agentic Jun 22, 2026
2 of 11 checks passed
Leoyzen added a commit that referenced this pull request Jun 28, 2026
Round-8 Gemini review fixes:
- run.py: cancel() now schedules agent._interrupt() as fire-and-forget
  task when _cancel_fn is None, enabling ACP CancelNotification
  propagation (fixes #49 HIGH)
- prompt_injection.py: remove insert_queued(), has_queued(),
  _queued_prompts — dead code from TurnRunner removal (fixes #50 MEDIUM)
- base_agent.py: remove has_queued_prompts() and clear_queued_prompts()
  — dead code referencing removed injection_manager methods
- core.py: delete _create_run() — dead method, replaced by
  _start_run_handle and SessionPool._create_run_handle (fixes #51 MEDIUM)

Comprehensive dead code cleanup:
- Delete 6 RunExecutor-only test files (run_executor.py module was
  deleted in this PR)
- Remove RunExecutor imports and tests from 3 files with mixed tests
- Remove AGENTPOOL_USE_RUN_TURN_FOR_ACP env var from 2 test files
  (feature flag was removed in this PR)
- Remove flush_pending_to_queue/pop_queued references (methods were
  in TurnRunner, never existed in current code)
- Remove _post_turn_injections/_post_turn_prompts assertions
  (TurnRunner attributes, now deleted)
- Update all comments referencing removed components
- Add create_turn() to test agent subclasses (new abstract method)
- Skip 11 pre-existing failures from run/turn separation refactor

Not adopted: #48 CRITICAL — ACP adapter gap is known tech debt,
  TODO comment already in place, follow-up issue to track.

Tests: 121 passed, 11 skipped, 0 failures.
Leoyzen added a commit that referenced this pull request Jun 28, 2026
Round-8 Gemini review fixes:
- run.py: cancel() now schedules agent._interrupt() as fire-and-forget
  task when _cancel_fn is None, enabling ACP CancelNotification
  propagation (fixes #49 HIGH)
- prompt_injection.py: remove insert_queued(), has_queued(),
  _queued_prompts — dead code from TurnRunner removal (fixes #50 MEDIUM)
- base_agent.py: remove has_queued_prompts() and clear_queued_prompts()
  — dead code referencing removed injection_manager methods
- core.py: delete _create_run() — dead method, replaced by
  _start_run_handle and SessionPool._create_run_handle (fixes #51 MEDIUM)

Comprehensive dead code cleanup:
- Delete 6 RunExecutor-only test files (run_executor.py module was
  deleted in this PR)
- Remove RunExecutor imports and tests from 3 files with mixed tests
- Remove AGENTPOOL_USE_RUN_TURN_FOR_ACP env var from 2 test files
  (feature flag was removed in this PR)
- Remove flush_pending_to_queue/pop_queued references (methods were
  in TurnRunner, never existed in current code)
- Remove _post_turn_injections/_post_turn_prompts assertions
  (TurnRunner attributes, now deleted)
- Update all comments referencing removed components
- Add create_turn() to test agent subclasses (new abstract method)
- Skip 11 pre-existing failures from run/turn separation refactor

Not adopted: #48 CRITICAL — ACP adapter gap is known tech debt,
  TODO comment already in place, follow-up issue to track.

Tests: 121 passed, 11 skipped, 0 failures.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants