SIGN IN SIGN UP

fix(loop): Responses degenerate retry — honor custom_tool_call, keep every attempt's usage

#1862: two defects at the same degenerate-retry boundary on the Responses wire.

A (eligibility): adapter-responses forwarded custom_tool_call lifecycle frames as
inert meta without counting the call, so a completed reasoning+custom-tool turn
(no text delta) satisfied the degenerate gate (calls.length === 0) and the request
was re-issued even though executable output had already reached the client. Count
the completed custom_tool_call like function_call (toolCallsEmitted++ plus a
passthrough tool_call) so it suppresses the false retry; passthrough keeps the wire
bytes verbatim and prevents a duplicate re-emission.

B (accounting): recordUsage runs once per outer round, after the inner retry loop's
usage reset, so a successful retry silently dropped the replaced attempt's measured
usage. Settle each attempt's usage the moment a retry replaces it (success path only);
a failed retry never reaches the settle, so the surviving attempt is recorded once —
no double count. The client-facing completion still carries only the final attempt's
usage (emitCompletion reads `usage`, which this never mutates).

Adds regression guards A1/B1/B2 to loop-degenerate-retry.test.ts; A1 and B1 fail on
the pre-fix code, B2 pins the failure path against over-counting/fabrication.
E
ework-agent committed
8120b14db7d595052e1f2185bc11231a031e99d2
Parent: 815027f