Follow-up hardening on #92977 (issue #92976). The cherry-picked retry
wrapped every verb, so an ECONNRESET arriving after the backend had
already processed a POST (prompt submitted, session created) would
silently double-submit on retry.
- Extract the transport policy into electron/api-transport.ts so it is
unit-testable without Electron: keep-alive agent pools, transient
error classification, and a verb-gated withRetry.
- Retry rule: GET/HEAD/OPTIONS retry on any transient transport error;
POST/PUT/PATCH/DELETE retry only when the request provably never
reached the server (connect-phase failures like ECONNREFUSED /
ENOTFOUND, or an error thrown before the body was flushed —
requestState.bodySent === false). Ambiguous resets after the body
went out surface to the caller; when in doubt, don't retry.
- Separate keep-alive pools for JSON calls vs streaming downloads so
long downloads can't starve latency-sensitive JSON calls.
- Destroy pooled agents on app will-quit.
- Tests: shouldRetryRequest truth table, withRetry behavior, plus LIVE
transport tests against real misbehaving node HTTP servers: a GET
burst where the server resets keep-alive sockets (bare attempt fails,
retried succeeds) and a POST whose socket is RST after server-side
processing (hit counter stays 1 — no double submit).