fix(sse): stop re-prepending Kiro tool docs onto every subsequent turn (#13652) - #13808
Merged
diegosouzapw merged 2 commits intoSep 16, 2026
Merged
Conversation
#13652) convertMessages() in openai-to-kiro.ts is stateless per HTTP request. Since OpenAI-compatible clients resend the full growing message array on every turn, the same tool-bearing first user message got re-scanned on every request and its relocated documentation (toolDocs) rebuilt from scratch; buildKiroPayload() then unconditionally prepended it onto whatever the newest turn was, so the ~10KB+ doc block kept landing on the current turn instead of staying anchored to the turn that originally carried it. Track the actual turn object that carries the relocated docs (toolDocsCarrier). When that turn is demoted into history on a later request (instead of being promoted to currentMessage), embed the doc text directly on that history entry and clear the local toolDocs so buildKiroPayload's existing prepend does not also inject it — docs now reach the model exactly once, anchored to their original turn. Regression test: tests/unit/issue-13652-kiro-tooldocs-repeat.test.ts (RED on unfixed code: turn-2 currentMessage still carried the full doc block; GREEN after the fix, with the doc anchored in history[0] instead).
diegosouzapw
force-pushed
the
fix/13652-kiro-tooldocs-repeat-every-turn
branch
from
September 15, 2026 22:56
73b8888 to
f221c5f
Compare
…-turn (base-red fix #13747)
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
diegosouzapw#13652) (diegosouzapw#13808) Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #13652
Root cause (short)
convertMessages()inopen-sse/translator/request/openai-to-kiro.tsis stateless per HTTPrequest. OpenAI-compatible clients resend the full growing message array on every turn, so the
same tool-bearing first user message gets re-scanned on every request and its relocated
documentation (
toolDocs) rebuilt from scratch.buildKiroPayload()then unconditionallyprepended it onto whatever the current turn was, so the ~10KB+ doc block kept landing on the
newest user message on every subsequent turn instead of staying anchored to the turn that
originally carried it.
Fix
Track the actual turn object that ends up carrying the relocated docs (
toolDocsCarrier), set atboth attachment sites in
convertMessages(). AftercurrentMessageis finalized, if thedocs-bearing turn was not promoted to
currentMessage(i.e. it was demoted intohistoryona later turn), embed the doc text directly onto that history entry's own content and clear the
local
toolDocssobuildKiroPayload()'s existing prepend does not also inject it. When thedocs-bearing turn IS
currentMessage(single-turn conversations, or the "no user turn" fallback),behavior is unchanged. Net effect: the doc block still reaches the model on every request (no
regression of the earlier
_toolDocsbug that dropped documentation entirely), but now exactlyonce per request, anchored to the turn it was originally attached to instead of re-glued onto the
newest turn.
This does not reduce the total bytes sent to Kiro per request (same total payload size) — it only
fixes where the doc block lands. Cutting the actual byte/token cost would require confirming
whether Kiro's
conversationId-scoped Builder ID cache retains prior-turn content server-side,which is unverified from the repo alone and out of scope here (flagged as a follow-up).
Regression test
tests/unit/issue-13652-kiro-tooldocs-repeat.test.tsRED (unfixed code):
GREEN (fixed code):
Gates run
npx eslint --suppressions-location config/quality/eslint-suppressions.json open-sse/translator/request/openai-to-kiro.ts tests/unit/kiro-long-tool-description-docs.test.ts tests/unit/issue-13652-kiro-tooldocs-repeat.test.ts→ clean, no outputnpm run check:open-sse-typecheck→openSseTypecheckErrors=0node scripts/check/check-file-size.mjs→ no violation on touched files (one pre-existing, unrelated frozen-file violation inopen-sse/utils/stream.ts, not touched by this PR)node scripts/check/check-complexity.mjs→ OK — 2824 violations (baseline 3218)node scripts/check/check-cognitive-complexity.mjs→ OK — 1276 violations (baseline 1437)node scripts/check/check-test-discovery.mjs→ OK, new test file discoveredDATA_DIR=$(mktemp -d) node --import tsx/esm --test --test-force-exit tests/unit/issue-13652-kiro-tooldocs-repeat.test.ts→ 1/1 passDATA_DIR=$(mktemp -d) node --import tsx/esm --test --test-force-exit tests/unit/kiro-long-tool-description-docs.test.ts→ 6/6 passDATA_DIR=$(mktemp -d) node --import tsx/esm --test --test-force-exit tests/unit/executor-kiro.test.ts→ 20/20 passopenai-to-kiro/buildKiroPayload/convertMessages(kiro-interleaved-tool-results-8903,kiro-model-aliases,kiro-system-reminder-2306,openai-to-kiro-helpers-split,translator-openai-to-kiro,openai-responses-reasoning-effort,repro-6576-kiro-thinking-unsupported-model,request-dedup-10249,translator-ai-sdk-image-parts,kiro-continue-filler-5231) → 80/80 passExisting tests aligned
tests/unit/kiro-long-tool-description-docs.test.ts— two tests updated:"relocated tool documentation reaches the current turn for every turn shape"→ renamed to"relocated tool documentation reaches exactly one turn for every turn shape". It previouslyasserted the doc block always lands on
currentMessage.contentfor everyTURN_SHAPESentry —that encoded the pre-fix bug for the multi-turn shapes (where the tool-bearing turn is demoted
into
history). Now it asserts the doc block reaches exactly one turn acrosshistory + currentMessagecombined, which is the actual regression guard for fix(providers): Kiro translator re-prepends full relocated tool documentation on every turn #13652 (catchesboth silent dropping and re-injection).
"only oversized descriptions are relocated in a mixed tool inventory"→ the assertions thatchecked
current.contentfor the relocated doc section now check the combinedhistory + currentMessagecontent, since the "multi-turn conversation" shape it uses nowanchors the doc in
history[0].Both changes are alignments to the corrected contract, not weakenings — no assertion was removed
or loosened; the "exactly once" check is strictly more specific than the prior "current turn
contains it" check.