Repository navigation
fix(kiro): send through provider egress and rotate hosts on gateway errors #5981
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -334,12 +334,14 @@ MiMo model Command Code serves. | |
| truncated tool JSON, and estimates usage because the upstream does not return token counts. | ||
| - Uses the configured `baseUrl` verbatim when it is custom. A canonical | ||
| `runtime.{region}.kiro.dev` URL follows the imported credential's API region; only that canonical | ||
| shape is eligible for one bounded fallback to `q.{region}.amazonaws.com` after an endpoint, | ||
| signature, DNS, or connection failure. | ||
| shape is eligible for one budgeted fallback to `q.{region}.amazonaws.com` after an endpoint, | ||
| signature, DNS, or connection failure, or HTTP 502/503/504 received before output. | ||
| - Owns replay-safe connection-reset recovery, that single eligible endpoint fallback, one OAuth | ||
| refresh/replay after HTTP 401, and bounded recovery for transient Kiro 429s. A shared cooldown and | ||
| single post-cooldown probe prevent concurrent requests from exhausting independent retry budgets; | ||
| hard quota failures and ordinary service errors are not replayed. | ||
| hard quota failures and other service errors are not replayed. Every Kiro physical send uses | ||
| configured provider egress. A header deadline returns 504; caller cancellation stops the turn. | ||
| Final HTTP 5xx bodies use fixed public text without upstream detail. | ||
|
Comment on lines
+337
to
+344
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
fd adapters.md docs-site/src/content/docs | xargs rg -n -C2 'q\.\{region\}\.amazonaws\.com|502|504'Repository: lidge-jun/opencodex Length of output: 5254 🏁 Script executed: #!/bin/bash
set -eu
for locale in ja ko ru zh-cn; do
file=$(fd -t f '^adapters\.md$' "docs-site/src/content/docs/$locale" | head -n 1)
echo "=== $file ==="
rg -n -i -C8 'kiro|runtime\.\{region\}|q\.\{region\}|502|503|504|フォールバック|резерв|fallback|대체|回退' "$file" || true
doneRepository: lidge-jun/opencodex Length of output: 22906 🏁 Script executed: set -eu
for locale in ja ko ru zh-cn; do
file=$(fd -t f '^adapters\.md$' "docs-site/src/content/docs/$locale" | head -n 1)
echo "=== $file ==="
rg -n -i -C8 'kiro|runtime\.\{region\}|q\.\{region\}|502|503|504|フォールバック|резерв|fallback|대체|回退' "$file" || true
doneRepository: lidge-jun/opencodex Length of output: 22906 Synchronize the Kiro sections in the remaining locale pages. The Kiro sections in Update these four sections to match 🤖 Prompt for AI AgentsSource: Path instructions |
||
| - Its non-streaming parser drains the same event stream for the web-search loop. | ||
| - Reports per-account usage. `AmazonCodeWhispererService.GetUsageLimits` on | ||
| `https://management.{region}.kiro.dev/` returns the plan allowance, which becomes the | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -209,15 +209,15 @@ birlikte Bearer olarak Kiro OAuth erişim belirteci. | |
| belirteç sayılarını döndürmediği için kullanımı tahmin eder. | ||
| - Özel olduğunda yapılandırılmış `baseUrl`'i birebir kullanır. Kurallı bir | ||
| `runtime.{region}.kiro.dev` URL'si içe aktarılan kimlik bilgisinin API | ||
| bölgesini takip eder; bir uç nokta, imza, DNS veya bağlantı hatasından sonra | ||
| bölgesini takip eder; bir uç nokta, imza, DNS veya bağlantı hatasından ya da çıktı başlamadan alınan HTTP 502/503/504 yanıtından sonra | ||
| `q.{region}.amazonaws.com`'a tek bir sınırlı geri dönüş için yalnızca bu | ||
| kurallı şekil uygundur. | ||
| - Yeniden oynatma güvenli bağlantı sıfırlama kurtarmasına, bu tek uygun uç nokta | ||
| geri dönüşüne, HTTP 401'den sonra bir OAuth yenileme/yeniden oynatmaya ve | ||
| geçici Kiro 429'ları için sınırlı kurtarmaya sahiptir. Paylaşılan bir soğuma | ||
| süresi ve soğuma sonrası tek bir araştırma, eşzamanlı isteklerin bağımsız | ||
| yeniden deneme bütçelerini tüketmesini önler; sabit kota hataları ve sıradan | ||
| hizmet hataları yeniden oynatılmaz. | ||
| diğer hizmet hataları yeniden oynatılmaz. Tüm Kiro gönderimleri yapılandırılmış sağlayıcı çıkışını kullanır; başlık zaman aşımı 504 döndürür, istemci iptali isteği durdurur ve son HTTP 5xx gövdeleri sabit genel metin kullanır. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: sed -n '208,224p' docs-site/src/content/docs/tr/reference/adapters.md
sed -n '333,347p' docs-site/src/content/docs/reference/adapters.mdRepository: lidge-jun/opencodex Length of output: 2754 🏁 Script executed: #!/bin/bash
set -o pipefail
printf '%s\n' '--- Turkish and English numbered context ---'
nl -ba docs-site/src/content/docs/tr/reference/adapters.md | sed -n '216,222p'
nl -ba docs-site/src/content/docs/reference/adapters.md | sed -n '337,347p'
printf '%s\n' '--- PR diff for the Turkish page ---'
git diff --unified=5 6581b561a710026d48e6908c9a0cdc3a36043812 579a82eab056d380e3c88e9404bd562bfdf78482 -- docs-site/src/content/docs/tr/reference/adapters.mdRepository: lidge-jun/opencodex Length of output: 4312 Remove the “sıradan” qualifier. The English source states that all other service errors are not replayed. The Turkish text limits this statement to “ordinary other service errors,” which can imply that other service errors may be replayed. Remove the qualifier to keep the translation aligned with the canonical English documentation. Suggested fix- yeniden deneme bütçelerini tüketmesini önler; sabit kota hataları ve sıradan
- diğer hizmet hataları yeniden oynatılmaz.
+ yeniden deneme bütçelerini tüketmesini önler; sabit kota hataları ve
+ diğer hizmet hataları yeniden oynatılmaz.🧰 Tools🪛 LanguageTool[misspelling] ~220-~220: Söz ve sayı arasında defis yoqtur: (COMPLEX_NUMBER_DEFIS_MISSING) 🤖 Prompt for AI Agents |
||
| - Akışsız ayrıştırıcısı web arama döngüsü için aynı olay akışını boşaltır. | ||
|
|
||
| ### Tamamlama anlambilimi | ||
|
|
@@ -340,4 +340,3 @@ Vizyon duyarlı adaptörler tarafından kullanılan paylaşılan yardımcılar: | |
| - `contentPartsToText(content)` — salt metin araç mesajları için içerik | ||
| parçalarını metne düzleştirir (açıklanmayan bir görsel kısa bir `[image]` | ||
| işaretçisi haline gelir, asla belirteç patlatan bir base64 bloğu olmaz). | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,6 +29,7 @@ const ENDPOINT_ERROR_MARKERS = [ | |
| "unsupported endpoint", | ||
| ]; | ||
| const CONNECT_ERROR_CODES = new Set(["ENOTFOUND", "EAI_AGAIN", "ECONNREFUSED", "ENETUNREACH", "EHOSTUNREACH"]); | ||
| type KiroFetchContext = AdapterFetchContext & { allowGatewayRotation?: boolean }; | ||
|
|
||
| interface KiroThrottleProbe { | ||
| token: symbol; | ||
|
|
@@ -164,6 +165,12 @@ async function fetchWithResetRecovery( | |
| ): Promise<Response> { | ||
| let lastError: unknown; | ||
| for (let attempt = 0; attempt < RESET_ATTEMPTS; attempt++) { | ||
| if (ctx.abortSignal?.aborted) throw abortError(ctx.abortSignal); | ||
| const executor = (ctx.executor ?? globalThis.fetch) as typeof globalThis.fetch & { | ||
| waitForPacing?: (signal?: AbortSignal) => Promise<void>; | ||
| unpacedFetch?: typeof globalThis.fetch; | ||
| }; | ||
| await executor.waitForPacing?.(ctx.abortSignal); | ||
| if (ctx.abortSignal?.aborted) throw abortError(ctx.abortSignal); | ||
| // Every physical send is admitted, not just the adapter entry. Kiro nests a throttle loop | ||
| // over this ladder and can run the ladder twice per throttle round, so counting one entry | ||
|
|
@@ -184,7 +191,18 @@ async function fetchWithResetRecovery( | |
| headers, | ||
| body: request.body, | ||
| ...(recovered ? { keepalive: false } : {}), | ||
| }, timeoutMs, ctx.abortSignal, ctx.stream); | ||
| }, timeoutMs, ctx.abortSignal, ctx.stream, (async (input, init) => { | ||
| try { | ||
| return await (executor.unpacedFetch ?? executor)(input, init); | ||
| } catch (error) { | ||
| if (ctx.abortSignal?.aborted) throw abortError(ctx.abortSignal); | ||
| const signal = init?.signal; | ||
| if (signal?.aborted && signal.reason instanceof Error && signal.reason.name === "TimeoutError") { | ||
| throw signal.reason; | ||
| } | ||
| throw error; | ||
| } | ||
| }) as typeof globalThis.fetch); | ||
| } catch (error) { | ||
| if (ctx.abortSignal?.aborted || !isConnectionResetError(error) || attempt === RESET_ATTEMPTS - 1) throw error; | ||
| lastError = error; | ||
|
|
@@ -255,19 +273,28 @@ async function inspectKiroThrottle( | |
|
|
||
| async function fetchKiroAttempt( | ||
| request: AdapterRequest, | ||
| ctx: AdapterFetchContext, | ||
| ctx: KiroFetchContext, | ||
| timeoutMs: number, | ||
| notePhysicalSend: (reset: boolean) => void, | ||
| ): Promise<Response> { | ||
| const legacy = legacyUrl(request.url); | ||
| const plannedUrl = request.url; | ||
| const legacy = legacyUrl(plannedUrl); | ||
| let response: Response; | ||
| try { | ||
| response = await fetchWithResetRecovery(request, request.url, ctx, timeoutMs, notePhysicalSend); | ||
| response = await fetchWithResetRecovery(request, plannedUrl, ctx, timeoutMs, notePhysicalSend); | ||
| } catch (error) { | ||
| if (!legacy || !endpointConnectFailure(error)) throw error; | ||
| return fetchWithResetRecovery(request, legacy, ctx, timeoutMs, notePhysicalSend); | ||
| } | ||
|
|
||
| if (!response.ok && (response.status === 502 || response.status === 503 || response.status === 504)) { | ||
| const baseUrl = response.url || request.url || plannedUrl; | ||
| const alternate = ctx.allowGatewayRotation === false ? undefined : legacyUrl(baseUrl); | ||
| if (alternate) { | ||
| cancelResponseBodyBestEffort(response); | ||
| return fetchWithResetRecovery(request, alternate, ctx, timeoutMs, notePhysicalSend); | ||
| } | ||
| } | ||
| if (legacy && !response.ok) { | ||
| const inspected = await inspectEndpointHttpFailure(response, ctx.abortSignal); | ||
| response = inspected.response; | ||
|
|
@@ -284,7 +311,7 @@ async function fetchKiroAttempt( | |
| * throttle recovery. The shared probe starts only after a 429, so healthy parallel traffic remains | ||
| * parallel while a throttled account cannot burn every caller's independent retry budget (#532). | ||
| */ | ||
| export async function fetchKiroWithRetry(request: AdapterRequest, ctx: AdapterFetchContext = {}): Promise<Response> { | ||
| export async function fetchKiroWithRetry(request: AdapterRequest, ctx: KiroFetchContext = {}): Promise<Response> { | ||
| const timeoutMs = ctx.timeoutMs ?? 200_000; | ||
| let probeToken: symbol | undefined; | ||
| // One ordinal sequence for the whole call, across the throttle loop, the endpoint fallback | ||
|
|
@@ -334,6 +361,10 @@ export async function fetchKiroWithRetry(request: AdapterRequest, ctx: AdapterFe | |
| throw new Error("Kiro throttle retry loop exhausted without a response"); | ||
| } catch (error) { | ||
| releaseKiroThrottleProbe(probeToken); | ||
| if (ctx.abortSignal?.aborted) throw abortError(ctx.abortSignal); | ||
| if (error instanceof Error && error.name === "TimeoutError") { | ||
| return new Response("Kiro upstream gateway timeout", { status: 504 }); | ||
| } | ||
| throw error; | ||
| } | ||
| } | ||
|
Comment on lines
361
to
370
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: sed -n '145,220p' src/adapters/kiro-retry.ts
sed -n '300,375p' src/adapters/kiro-retry.tsRepository: lidge-jun/opencodex Length of output: 6451 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- relevant symbols ---'
rg -n --glob '!node_modules' --glob '!dist' 'fetchWithAttemptDeadline|TimeoutError|executor\??|unpacedFetch|fetchKiroWithRetry' src tests structure
printf '%s\n' '--- file map ---'
fd -t f . src/adapters tests | rg 'kiro|adapter|fetch|timeout' | head -80Repository: lidge-jun/opencodex Length of output: 41991 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- kiro imports and helpers ---'
sed -n '1,170p' src/adapters/kiro-retry.ts
printf '%s\n' '--- deadline helper definitions ---'
rg -n -S --glob '!node_modules' --glob '!dist' 'fetchWithAttemptDeadline|fetchWithHeaderTimeout|AttemptDeadline|HeaderTimeout' .
printf '%s\n' '--- Kiro tests around timeout/executor ---'
rg -n -C 8 'TimeoutError|executor|abort|deadline|504' tests/providers/kiro tests/adapters src/adapters/kiro-retry.ts
printf '%s\n' '--- Kiro contract ---'
sed -n '1,115p' structure/providers/kiro.mdRepository: lidge-jun/opencodex Length of output: 42759 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- attempt deadline implementation ---'
sed -n '430,510p' src/lib/upstream-retry.ts
printf '%s\n' '--- adapter context contract ---'
rg -n -A35 -B8 'interface AdapterFetchContext|type AdapterFetchContext|executor\??:' src/adapters/base.ts src/server/responses/adapter-dispatch.ts src/server/responses/fetch-helpers.ts
printf '%s\n' '--- source TimeoutError uses ---'
rg -n -C 6 'TimeoutError' src/lib src/adapters src/server src/providers tests/providers/kiro/kiro-retry.test.ts tests/providers/kiro/kiro-transport-parity.test.ts
printf '%s\n' '--- exact Kiro timeout tests ---'
rg -n -A30 -B12 '504|TimeoutError|timeout' tests/providers/kiro/kiro-retry.test.ts tests/providers/kiro/kiro-transport-parity.test.tsRepository: lidge-jun/opencodex Length of output: 42211 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- clearable deadline ---'
rg -n -A70 -B10 'clearableDeadline' src/lib src
printf '%s\n' '--- provider fetch declaration and implementation ---'
rg -n -A50 -B15 'providerFetch|type ProviderFetch|interface ProviderFetch' src/server src/lib src/providers
printf '%s\n' '--- TimeoutError uses in runtime source ---'
rg -n -C 8 'TimeoutError' src --glob '*.ts'Repository: lidge-jun/opencodex Length of output: 45666 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- providerFetch definition files ---'
rg -l 'providerFetch' src --glob '*.ts'
printf '%s\n' '--- providerFetch declarations ---'
rg -n 'export .*providerFetch|function providerFetch|const providerFetch|type ProviderFetch|interface ProviderFetch' src --glob '*.ts'
printf '%s\n' '--- fetch helper deadline body ---'
sed -n '358,420p' src/server/responses/fetch-helpers.ts
printf '%s\n' '--- provider transport files ---'
fd -t f . src | rg 'provider|proxy|fetch|transport' | head -120Repository: lidge-jun/opencodex Length of output: 7203 🏁 Script executed: #!/bin/bash
set -e
sed -n '100,270p' src/server/responses/fetch-helpers.tsRepository: lidge-jun/opencodex Length of output: 9683 🏁 Script executed: #!/bin/bash
set -e
sed -n '270,355p' src/server/responses/fetch-helpers.tsRepository: lidge-jun/opencodex Length of output: 4003 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- provider config fetch contract ---'
rg -n -A30 -B15 'interface OcxProviderConfig|type OcxProviderConfig|fetch\?:' src
printf '%s\n' '--- Kiro adapter binding ---'
rg -n -A35 -B20 'fetchKiroWithRetry|fetchResponse' src/adapters/kiro src/adapters/kiro.ts
printf '%s\n' '--- exact changed hunk ---'
git diff 6581b561a710026d48e6908c9a0cdc3a36043812 579a82eab056d380e3c88e9404bd562bfdf78482 -- src/adapters/kiro-retry.tsRepository: lidge-jun/opencodex Length of output: 41792 Map only the Kiro deadline to HTTP 504. If Suggested fix+class KiroHeaderTimeoutError extends Error {
+ constructor(cause: Error) {
+ super(cause.message, { cause });
+ this.name = "KiroHeaderTimeoutError";
+ }
+}
+
...
const signal = init?.signal;
if (signal?.aborted && signal.reason instanceof Error && signal.reason.name === "TimeoutError") {
- throw signal.reason;
+ throw new KiroHeaderTimeoutError(signal.reason);
}
...
- if (error instanceof Error && error.name === "TimeoutError") {
+ if (error instanceof KiroHeaderTimeoutError) {
return new Response("Kiro upstream gateway timeout", { status: 504 });
}🤖 Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This added
_planentry publishes pre-release credential-mixing analysis, dispatch-override behavior, and an old-bearer test plan while the corresponding fix is still under review. Becausedevlog/is tracked and public, move these working notes to.tmp/or other scratch space and publish only the shipped outcome (under_fin/if a retrospective is needed); the repository explicitly prohibits unreleased security findings and pre-disclosure patch plans indevlog/.AGENTS.md reference: AGENTS.md:L129-L136
Useful? React with 👍 / 👎.