Three tests covering the CC→Responses→Anthropic finalize path:
- TestStreamingParallelToolUseNoGhostDelta: full end-to-end with two
parallel tool_calls, asserts every content_block_delta targets a
started block
- TestStreamingParallelToolUseSecondToolPackedArgsDone: focused test
where tool 1 streams deltas and tool 2 has packed .done args
- TestStreamingThreeParallelToolsAllPackedDone: three tools all with
packed .done, the most extreme case
All three fail on unfixed code (ghost deltas on wrong indices) and
pass after the fix.
resToAnthHandleFuncArgsDone used state.ContentBlockIndex directly
instead of looking up from OutputIndexToBlockIdx like
resToAnthHandleFuncArgsDelta does. When multiple tool_calls arrive
in parallel and arguments come as a packed .done (no prior delta),
the second+ tool would emit content_block_delta on an index that
was never content_block_start'ed, causing Claude Code to report
"Content block not found".
Fix: resolve block index from OutputIndexToBlockIdx, and skip the
delta if the block is already closed or the index doesn't match
the current open block.
Closes#4193
WSv2 egress relays upstream events verbatim without the HTTP-path
namespace restore, so flattening requests that take the WSv2 branch
would surface flattened tool names the client cannot match. Resolve
the WS transport decision before flattening and skip flattening only
when the request will actually go WSv2 (passthrough accounts return
via HTTP before the WSv2 branch and still flatten).
Also check all type assertions in responses_namespace_test.go to
satisfy golangci-lint errcheck.
Resolve conflict in openai_gateway_passthrough.go streaming path: keep
main's normalizeCompletedImageGenerationStatus normalization ahead of
this branch's namespace restore block, mirroring the established order
in openai_gateway_response_handling.go.
The snapshot taken before dispatching the OAuth response handler still used
raw c.Writer.Size(), while handleOpenAIImagesOAuthResponseError compares it
against the keepalive-adjusted size. From the second failover round on, the
previous round's heartbeat bytes made the raw snapshot positive while the
adjusted size stayed -1, so a retryable upstream error was misjudged as
'response already written': the failover was swallowed and the client got a
committed 200 with a whitespace-only body.
Snapshot now uses OpenAIImagesJSONKeepaliveAdjustedWrittenSize, matching the
comparison side. Regression test simulates a prior-round heartbeat before
ForwardImages and asserts the retryable error still converts to
UpstreamFailoverError (verified red without the fix, green with it).