## Problem
When the retry loop's `_summarize_api_error()` receives an exception
with an empty `str(error)` (bare `raise AssertionError()`, bare `raise`
without args, or third-party SDK accumulators that discard the original
payload), users see:
📝 Error:
...with nothing after the colon, and no way to diagnose the root cause
without re-running under a debugger.
## Real-world trigger
The anthropic SDK's streaming accumulator raises:
RuntimeError('Unexpected event order, got error before "message_start"')
when Bedrock or Anthropic returns a service-level `error` event as the
first stream event (throttling, overload, 5xx, etc.). The SDK has the
error payload in hand but throws it away before raising — all the user
sees is the cryptic "event order" message with zero context. Even
worse, bare `AssertionError()`s in user-authored code paths often have
empty messages entirely.
## Fix
When the error's string representation is empty or whitespace-only,
walk `error.__traceback__` and format the last frame as:
AssertionError at /path/file.py:123 in some_method() — source line
This gives users an actionable locator without changing the output for
any error with a real message.
## Changes
- `run_agent.py` — `_summarize_api_error()`: added empty-payload guard
at the top of the function before the existing Cloudflare-HTML path.
- `tests/agent/test_summarize_api_error.py` — 4 new tests covering
empty AssertionError, empty-message RuntimeError without traceback,
whitespace-only message, and the fall-through path (non-empty errors
must not be affected).
## Tests
pytest tests/agent/test_summarize_api_error.py -v
# 4 passed in 4.88s
## Risk
- Zero impact on any error with a non-empty message — the new branch
only fires when `str(error).strip()` is falsy.
- `traceback` is a stdlib module; import is lazy (inside the branch).
- `getattr(error, "__traceback__", None)` is the documented API and
handles manually-constructed exceptions that were never raised.
Signed-off-by: Andre Kurait <andrekurait@gmail.com>