fix(responses-continuation): chain off the effective post-reconstruction input, not the pre-reconstruction client bytes - #12641
Merged
diegosouzapw merged 1 commit intoSep 4, 2026
Conversation
…ion input, not the pre-reconstruction client bytes Root cause: resolvePreviousResponseState() reconstructs a continued turn's full history by reading a prior call-log's clientRawRequest.body.input. But clientRawRequest is deliberately captured BEFORE chat.ts's own previous_response_id reconstruction runs (captureDeferredClientRawBody's whole point -- it must preserve the raw client bytes for audit/guardrail purposes, not the request OmniRoute rewrote it into). For a prior turn that was itself a continuation, body.input is just the client's own trimmed delta, not the full input that actually dispatched. Chaining a later continuation off that instead of the real effective input compounds: each hop's stored "input" is only the prior hop's already-trimmed delta, so a few turns into a tool-heavy conversation the reconstruction degrades to a handful of bare tool items with no leading system/user message. Live incident (2026-09-03): Ping's 3rd turn in a session failed with a Gemini 400 -- "Please ensure that function call turn comes immediately after a user turn or after a function response turn" -- because OmniRoute sent exactly that malformed 3-item request (function_call_output, function_call, function_call_output, nothing before it) upstream. Fix: capture the actual effective `input` a request dispatches with -- after previous_response_id reconstruction runs, whether or not it applied this specific turn -- as a new sibling field on clientRawRequest (`effectiveInput`), threaded through the existing logClientRawRequest / logClientRawRequestRedacted call chain (no new parameter threading needed elsewhere). resolvePreviousResponseState now chains off effectiveInput, falling back to body.input only for artifacts logged before this field existed. Tests: tests/unit/responses-continuation-store.test.ts -- new case proves a continued turn correctly reconstructs full history from effectiveInput (fails on pre-fix code: returns just the 1-item delta instead of the 4-item reconstructed history); new case proves the body.input fallback still works for legacy artifacts lacking the field. Full file: 13/13 passing. tsc --pretty false -p tsconfig.typecheck-core.json clean.
hartmark
added a commit
to hartmark/OmniRoute
that referenced
this pull request
Sep 3, 2026
…he effective post-reconstruction input, not the pre-reconstruction client bytes) into dev/omniroute-dev-combined
hartmark
added a commit
to hartmark/OmniRoute
that referenced
this pull request
Sep 3, 2026
…he effective post-reconstruction input, not the pre-reconstruction client bytes) into dev/omniroute-dev-combined
hartmark
added a commit
to hartmark/OmniRoute
that referenced
this pull request
Sep 3, 2026
…he effective post-reconstruction input, not the pre-reconstruction client bytes) into dev/omniroute-dev-combined
hartmark
added a commit
to hartmark/OmniRoute
that referenced
this pull request
Sep 3, 2026
…he effective post-reconstruction input, not the pre-reconstruction client bytes) into dev/omniroute-dev-combined
diegosouzapw
merged commit Sep 4, 2026
74c2d26
into
diegosouzapw:release/v3.8.51
14 of 16 checks passed
diegosouzapw
pushed a commit
that referenced
this pull request
Sep 4, 2026
…mbo target retries (#12650) Validado em lote numa worktree combinada com os 3 PRs desta leva sobre o tip de `release/v3.8.51`: os três boardaram sem conflito, `typecheck:core` limpo e **22/22** nos arquivos de teste que trazem. O crescimento de `src/sse/handlers/chat.ts` (2450 → 2454) é do #12641 e vai num PR de rebaseline próprio. Obrigado, @hartmark.
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…ion input, not the pre-reconstruction client bytes (diegosouzapw#12641) Validado em lote numa worktree combinada com os 3 PRs desta leva sobre o tip de `release/v3.8.51`: os três boardaram sem conflito, `typecheck:core` limpo e **22/22** nos arquivos de teste que trazem. O crescimento de `src/sse/handlers/chat.ts` (2450 → 2454) é do diegosouzapw#12641 e vai num PR de rebaseline próprio. Obrigado, @hartmark.
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…mbo target retries (diegosouzapw#12650) Validado em lote numa worktree combinada com os 3 PRs desta leva sobre o tip de `release/v3.8.51`: os três boardaram sem conflito, `typecheck:core` limpo e **22/22** nos arquivos de teste que trazem. O crescimento de `src/sse/handlers/chat.ts` (2450 → 2454) é do diegosouzapw#12641 e vai num PR de rebaseline próprio. Obrigado, @hartmark.
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…-input persistence (diegosouzapw#12680) Rebaseline medido no tip com o diegosouzapw#12641 mergeado.
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.
What Problem This Solves
previous_response_idcontinuation (OmniRoute-native virtualization,src/lib/db/responsesContinuationStore.ts) silently corrupts conversationhistory a few turns into any tool-heavy exchange: the reconstructed request
degrades to a handful of bare tool-call items with no leading system/user
message, which the upstream provider then rejects outright.
Why This Change Was Made
resolvePreviousResponseState()reconstructs a continued turn's full historyby reading the prior call-log's
clientRawRequest.body.input. ButclientRawRequestis deliberately captured beforechat.ts's ownprevious_response_idreconstruction runs(
captureDeferredClientRawBody's whole point — it has to preserve the rawclient bytes for audit/guardrail purposes, not what OmniRoute rewrote the
request into).
For a prior turn that was itself a continuation,
body.inputis just theclient's own trimmed delta (relying on OmniRoute to have already
reconstructed history server-side) — not the full input that actually
dispatched. Chaining a later continuation off that instead of the real
effective input compounds: each hop's stored "input" is only the prior hop's
already-trimmed delta, so a few turns into a tool-heavy conversation the
reconstruction degrades to a handful of bare tool items with nothing before
them.
Live incident (2026-09-03): a real production session's 3rd turn failed
with a Gemini 400 —
Please ensure that function call turn comes immediately after a user turn or after a function response turn— because OmniRoutesent exactly that malformed 3-item request
(
function_call_output, function_call, function_call_output, no leadingsystem/user message) upstream.
Fix
Capture the actual effective
inputa request dispatches with — afterprevious_response_idreconstruction runs, whether or not it applied thisspecific turn — as a new sibling field on
clientRawRequest(
effectiveInput), threaded through the existinglogClientRawRequest/logClientRawRequestRedactedcall chain. No new parameter threading neededanywhere else in the request pipeline.
resolvePreviousResponseStatenow chains offeffectiveInput, falling backto
body.inputonly for artifacts logged before this field existed (nocompat migration needed — self-healing: any conversation naturally recovers
correct reconstruction the moment one of its turns is logged post-fix).
User Impact
previous_response_idno longer degrades orcorrupts history a few turns into a tool-heavy conversation.
effectiveInputalways equalsbody.inputin that case).Evidence
tests/unit/responses-continuation-store.test.ts:clientRawRequest.body" — proves a continued turn correctly reconstructs
the full 4-item history from
effectiveInputinstead of the 1-item rawdelta. Fails on pre-fix code (returns just the delta item); passes
after.
is absent (pre-fix artifacts)" — proves legacy artifacts without the new
field still resolve exactly as before (no regression for existing data).
Full file: 13/13 passing.
tsc --pretty false -p tsconfig.typecheck-core.jsonclean on all 4 touched production files.