From db040e70fe52a223c8d6e63bbc87a508f2ee7ae0 Mon Sep 17 00:00:00 2001 From: Ingwannu Date: Fri, 21 Aug 2026 18:09:06 +0000 Subject: [PATCH] fix(kiro): accept permissive parallel tool hints --- .../src/content/docs/reference/adapters.md | 4 ++ src/adapters/kiro.ts | 3 -- structure/04_transports-and-sidecars.md | 16 +++++++ tests/kiro-adapter.test.ts | 44 ++++++++++++++++++- 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/docs-site/src/content/docs/reference/adapters.md b/docs-site/src/content/docs/reference/adapters.md index 29859992f7..1b96ebac7a 100644 --- a/docs-site/src/content/docs/reference/adapters.md +++ b/docs-site/src/content/docs/reference/adapters.md @@ -147,6 +147,10 @@ of the HTTP retry loop. - Builds Kiro `conversationState`, maps Codex tools and tool results, and sends image blocks supported by the Kiro wire. +- Treats a client `parallel_tool_calls: true` value as permission rather than a wire requirement. + Kiro remains serialized: the routed catalog advertises no parallel-tool capability and the + adapter sends no parallel-control field upstream, but ordinary Codex tool turns are not rejected + solely because the client permits parallel calls. - Decodes `application/vnd.amazon.eventstream`, reconstructs text/thinking/tool events, detects 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 diff --git a/src/adapters/kiro.ts b/src/adapters/kiro.ts index 7fdfcc1db8..11ed49886e 100644 --- a/src/adapters/kiro.ts +++ b/src/adapters/kiro.ts @@ -318,9 +318,6 @@ function validateKiroCapabilities(parsed: OcxParsedRequest): void { if (choice !== undefined && choice !== "auto" && choice !== "none") { throw new Error("Kiro supports only automatic tool choice or tool_choice:none"); } - if (parsed.options.parallelToolCalls === true) { - throw new Error("Kiro does not support parallel tool calls"); - } if (parsed.options.serviceTier !== undefined) { throw new Error("Kiro does not support service tiers"); } diff --git a/structure/04_transports-and-sidecars.md b/structure/04_transports-and-sidecars.md index 75410d38b3..5ec8d0f967 100644 --- a/structure/04_transports-and-sidecars.md +++ b/structure/04_transports-and-sidecars.md @@ -823,6 +823,22 @@ Grounded in the open-sourced official client (xai-org/grok-build); unit + eviden `fetchWithHeaderTimeout` takes an executor so provider fetch wrappers stay inside the timeout race. +## Kiro client parallel-tool hint + +Kiro's wire remains serialized even when an OpenAI Responses client sends +`parallel_tool_calls: true`. That request field is permissive: it allows parallel calls but does not +require the routed transport to expose a matching flag. The Kiro catalog therefore continues to +advertise `supports_parallel_tool_calls: false`, and the adapter emits no parallel-control field, +while accepting the client hint and translating the ordinary tool catalog normally. + +[Decision Log] +- 목적과 의도: Keep current Codex clients usable with Kiro without claiming or inventing parallel execution on the CodeWhisperer wire. +- 기존 구현 및 제약 조건: Codex can send `parallel_tool_calls: true` even for catalog rows that advertise false; Kiro has no verified parallel-control request field and serializes tool execution. +- 검토한 주요 대안: Reject the client hint, rewrite it to false before routing, or accept it as permission while leaving the Kiro wire unchanged. +- 선택한 방식: Accept either request value, preserve the parsed client intent internally, and omit all parallel-control fields from the Kiro payload. +- 다른 대안 대신 이 방식을 선택한 이유: Rejection interprets permission as a requirement and blocks valid turns, while rewriting shared request state hides caller intent and can affect later policy or diagnostics. +- 장점, 단점 및 영향: Codex tool turns reach Kiro again and the adapter contract stays honest; Kiro still cannot produce true parallel tool batches through this transport. + ## Kiro reasoning round-trip (`redactedContent`) Kiro never returns plaintext reasoning for its **GPT-5.6 family** (`gpt-5.6-sol`, `-terra`, diff --git a/tests/kiro-adapter.test.ts b/tests/kiro-adapter.test.ts index 779816a559..770de3d8de 100644 --- a/tests/kiro-adapter.test.ts +++ b/tests/kiro-adapter.test.ts @@ -12,6 +12,7 @@ import { saveCredential } from "../src/oauth/store"; import { normalizeKiroModelId } from "../src/providers/kiro-models"; import { configuredReasoningEfforts, mapReasoningEffort } from "../src/reasoning-effort"; import { PROVIDER_REGISTRY } from "../src/providers/registry"; +import { parseRequest } from "../src/responses/parser"; import type { OcxParsedRequest, OcxProviderConfig } from "../src/types"; const origHome = process.env.HOME; @@ -835,7 +836,6 @@ describe("kiro adapter — buildRequest", () => { for (const options of [ { toolChoice: "required" }, { toolChoice: { name: "bash" } }, - { parallelToolCalls: true }, { serviceTier: "priority" }, ]) { await expect(createKiroAdapter(provider).buildRequest({ @@ -853,6 +853,48 @@ describe("kiro adapter — buildRequest", () => { const current = JSON.parse((await createKiroAdapter(provider).buildRequest(none)).body).conversationState.currentMessage.userInputMessage; expect(current.userInputMessageContext?.tools).toBeUndefined(); }); + + test("accepts Codex's permissive parallel-tool hint while keeping the Kiro wire serialized", async () => { + const parsed = parseRequest({ + model: "kiro/claude-haiku-4.5", + input: "test", + stream: true, + parallel_tool_calls: true, + tools: [{ + type: "function", + name: "bash", + description: "Run a shell command", + parameters: { type: "object" }, + }], + }); + expect(parsed.options.parallelToolCalls).toBe(true); + + const payload = JSON.parse((await createKiroAdapter(provider).buildRequest(parsed)).body) as { + parallel_tool_calls?: boolean; + parallelToolCalls?: boolean; + conversationState: { + parallel_tool_calls?: boolean; + parallelToolCalls?: boolean; + currentMessage: { + userInputMessage: { + userInputMessageContext?: { + parallel_tool_calls?: boolean; + parallelToolCalls?: boolean; + tools?: Array<{ toolSpecification?: { name?: string } }>; + }; + }; + }; + }; + }; + const context = payload.conversationState.currentMessage.userInputMessage.userInputMessageContext; + expect(context?.tools?.some(tool => tool.toolSpecification?.name === "bash")).toBe(true); + expect(payload.parallel_tool_calls).toBeUndefined(); + expect(payload.parallelToolCalls).toBeUndefined(); + expect(payload.conversationState.parallel_tool_calls).toBeUndefined(); + expect(payload.conversationState.parallelToolCalls).toBeUndefined(); + expect(context?.parallel_tool_calls).toBeUndefined(); + expect(context?.parallelToolCalls).toBeUndefined(); + }); }); describe("kiro adapter — native and emulated reasoning effort", () => {