Skip to content

Honor X-RateLimit-Reset header and extend retry budget to recover from AI provider 429s.#8

Merged
MicahZoltu merged 1 commit into
mainfrom
micah
Jun 17, 2026
Merged

Honor X-RateLimit-Reset header and extend retry budget to recover from AI provider 429s.#8
MicahZoltu merged 1 commit into
mainfrom
micah

Conversation

@MicahZoltu

Copy link
Copy Markdown
Member

Also extracts AI fetch retry logic into testable orchestration with injected leaf dependencies.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI Agent Review

The refactored createHttpFetch in source/ai.mts discards the AbortSignal parameter (named _signal), breaking in-flight HTTP request cancellation. The original createFetch forwarded signal to the native fetch() call, allowing abort to cancel ongoing network requests. In the new code, only the retry sleep respects the signal — actual in-flight requests run to completion even after abort fires. This is a functional regression that can leave connections open and consume resources after the caller has moved on (e.g., when the agentLoop idle timer fires).

Comment thread source/ai.mts Outdated
const url = `${configuration.apiUrl.replace(/\/$/, "")}/chat/completions`
const requestHeaders: Record<string, string> = { ...headers }
if (configuration.apiKey) requestHeaders["Authorization"] = `Bearer ${configuration.apiKey}`
return fetch(url, { method: "POST", headers: requestHeaders, body })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AbortSignal is received as _signal (explicitly unused) and never forwarded to fetch(). The original code passed signal to the native fetch() call, enabling in-flight request cancellation on abort. Without it, the signal only works during retry sleep phases — in-flight HTTP requests continue running even after abort, consuming resources and leaving connections open. Fix: pass signal to the fetch() call: return fetch(url, { method: "POST", headers: requestHeaders, body, signal })

…m AI provider 429s.

Also extracts AI fetch retry logic into testable orchestration with injected leaf dependencies.
@MicahZoltu

Copy link
Copy Markdown
Member Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI Agent Review

The refactoring of createFetch from createFetch(configuration: AiConfiguration) to createFetch(dependencies: FetchDependencies) leaks retry-implementation details through the abstraction boundary, forcing the composition root to wire up internals (sleep, random, now, httpFetch) that were previously encapsulated within createFetch.

Comment thread source/index.mts
const spawnGit = createSpawnGit(workspaceDirectory)
const aiConfiguration = parseAiConfiguration(Bun.env)
const fetch = createFetch(aiConfiguration)
const fetch = createFetch({ httpFetch: createHttpFetch(aiConfiguration), sleep: createSleep(), random: createRandom(), now: createNow() })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The composition root is now coupled to retry timing details (sleep, random, now) that are implementation details of the retry mechanism inside ai.mts. Previously, createFetch(aiConfiguration) encapsulated all retry behavior; now every call site must construct four dependencies. Consider keeping the convenience signature createFetch(aiConfiguration) as the production entry point (wiring up defaults internally) and exposing a separate createFetchWithDependencies(dependencies) for tests, so changes to retry internals don't ripple outward to index.mts.

@MicahZoltu MicahZoltu merged commit 1a79fd9 into main Jun 17, 2026
2 checks passed
@MicahZoltu MicahZoltu deleted the micah branch June 17, 2026 19:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant