OpenAI-compatible providers return 'stop' and 'tool_calls' as finish_reason
values, but Flynn's agent loop expects Anthropic-style 'end_turn' and
'tool_use'. This caused the agent to exit the tool loop prematurely when
falling back to GitHub Copilot (due to Anthropic API quota exhaustion).
- openai.ts: Map 'stop' → 'end_turn', 'length' → 'max_tokens', tool_calls
with actual tools → 'tool_use', tool_calls without tools → 'end_turn'
- github.ts: Handle edge case where finish_reason is 'tool_calls' but no
tools were parsed
- agent.ts: Accept both 'tool_use' and 'tool_calls' as valid stop reasons
(belt-and-suspenders), extract toolCalls to local variable for TS narrowing
- openai.test.ts: Update expectations to match new normalized values
Implements ModelClient interface with OpenAI SDK to support GPT models
as fallback when local inference is unavailable. Includes tests with
mocked OpenAI API responses.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>