Files
hermes-agent/tests
cdepuy dc4b88714b fix(gateway): honor server retry_after for flood-capped sends instead of plain-text fallback
Flood-capped / rate-limited sends (Telegram FloodWait, Weixin bare RuntimeError)
were not honored as retryable when retry_after was absent, so _send_with_retry
fell through to the truncating plain-text fallback, which re-entered the server
ban and dropped the message tail — silently burning the turn with no delivery.

Three changes to _send_with_retry:
1. Route rate-limit checks through _is_rate_limited_error (single wrapper over
   classify_send_error) so rate limits are treated as transient even when the
   platform omits retry_after.
2. Reclassify rate-limit per retry attempt, not just the initial send, so the
   in-loop break/continue reflects the CURRENT attempt (covers transient->flood
   and rate-limited->formatting transitions).
3. Never take the truncating plain-text fallback for a rate-limited send — return
   the typed failure so the delivery ledger owns redelivery after the cooldown.
   And when retries exhaust on a rate-limited / retry_after-carrying failure,
   return BEFORE sending the delivery-failure notice: the notice send would land
   inside the same flood penalty and re-enter the ban ([0, 189, 378, 378] -> 3
   sends, no 4th). Ordinary exhausted network errors keep the existing notice.

Honors server retry_after (when present) as the backoff delay instead of the
default exponential schedule.

Rebased onto current main (was 5163 commits behind, CONFLICTING) and folded in
reviewer feedback. Regression coverage in tests/gateway/test_send_retry.py:
rate-limit classifier, rate-limited-without-retry_after retries & succeeds,
rate-limited exhaustion returns a typed failure with no fallback and no notice
inside the active flood penalty, and failure-kind transitions between attempts.
2026-09-06 20:49:30 +05:30
..