`_prepare_inbound_message_text` (async) called `_decide_image_input_mode`
inline for every inbound image. That decision is synchronous and does
blocking network I/O on the way to a capability answer:
- `agent.models_dev.fetch_models_dev` — an HTTP GET to models.dev (15s
timeout) whenever the 1-hour in-memory cache is cold or models.dev is slow.
- `agent.model_metadata.query_ollama_supports_vision` — HTTP probes
(`detect_local_server_type` + `/api/show`) against a local Ollama server
when the active provider fronts one.
Running that inline blocks the gateway event loop for up to the request
timeout — so a single user attaching an image freezes EVERY session on that
gateway (no other messages processed, no heartbeats) until the fetch/probe
returns or times out. This is the same off-the-loop class as the cron-fire
verifier and the async_is_safe_url work.
Wrap the call in `asyncio.to_thread` so the blocking capability lookup runs
on a worker thread and the loop stays responsive. The decision result and
routing are unchanged.
Test: a gateway image-routing runtime test asserts the capability lookup runs
off the main (event-loop) thread; it runs on the main thread before the fix.