A real-model E2E (Ollama qwen3:8b) exposed what unit tests with synthetic
parts missed: `POST /session/{id}/message` returns only the final assistant
message (text/reasoning), while ToolParts live in intermediate assistant
messages. The parser was reading the wrong message, so tool_results was always
empty even when opencode actually edited files.
- run(): after the prompt POST, GET /session/{id}/message and collect parts
across the whole turn for tool extraction (content still comes from the
final message). Verified against a live opencode session.
- _extract_tool_results: success now keys on opencode's state.status ==
"completed" (states: completed | error | running | pending).
- Test now feeds the real full-turn message shape and asserts the write tool
is recovered.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>