httpx timeout exceptions (ReadTimeout/ConnectTimeout/PoolTimeout/WriteTimeout)
stringify to an EMPTY string, so when one survived the retry loop the user saw
'API call failed after 6 retries: ' with nothing after the colon — a TLS
abort, a reset connection, and a deterministic read-timeout fire were all
indistinguishable (and invisible).
Ported Buzz's pure timeout_message classifier (crates/buzz-agent/src/llm.rs,
block/buzz#4959) to hermes-agent:
- agent/timeout_error_summary.py: pure classifier over the exception type —
connect-phase timeouts ('no connection established, check base_url') vs
read-phase timeouts ('no response received within <N>s — consider raising
providers.<provider>.request_timeout_seconds in ~/.hermes/config.yaml'),
embedding the configured timeout value (per-model/per-provider config
first, HERMES_API_TIMEOUT fallback) and the exact config knob.
- run_agent.py: _summarize_api_error() checks the timeout classifier first
(all later branches produce blank output for message-less exceptions);
gains optional provider/model kwargs, backward compatible.
- agent/conversation_loop.py: the three retry-loop summary sites pass
provider/model context.
Tests: 12 new (pure classifier + AIAgent integration + empty-str
precondition pin). Sabotage-verified: disabling the classifier fails the
regression tests with the historical blank summary (assert '').