The Patrol runtime classifier collapsed three distinct upstream
conditions into one misleading "Selected model does not support
Patrol tools" message:
1. Provider rejected the *value* Pulse sent for tool selection
(e.g. DeepSeek's "deepseek-reasoner does not support this
tool_choice" — the model accepts tools, just not the forced
coercion). The DeepSeek fix in 46145df9 dodges the symptom by
coercing to auto, but the original misclassification pointed
operators at the wrong remediation for 33 days.
2. Provider has no tool-capable endpoint available for the
selected model (OpenRouter's "No endpoints found …" surfaces
this when account-level provider/data filters exclude every
tool-capable route).
3. Model truly lacks tool calling (the literal "tools are not
supported" / "tool calling" cases).
Each now has its own PatrolFailureCause, title, summary,
description, and recommendation. summarizePatrolRuntimeFailureDetail
mirrors the split. Helper predicates patrolToolChoiceValueRejected
and patrolNoToolCapableEndpoint encapsulate the substring matching.
The OpenRouter "No endpoints found" test fixture now correctly
classifies as no_tool_capable_endpoint instead of
model_unsupported_tools — fixture updates in
patrol_runtime_failure_test.go, patrol_assistant_handoff_test.go,
and ai_handler_test.go reflect the more accurate diagnostic.
New tests cover the tool_choice_rejected and generic
model_unsupported_tools paths explicitly.
The ai-runtime contract is updated to note the classifier-split
obligation alongside the existing transport-shape obligation.