- Maintain non-tool-result content blocks at their original indices in `AlignClaudeToolResults` instead of moving them after results.
- Reorder tool results in-place into their original slot indices based on preceding tool use IDs.
Closes: #5484
- Responses API: only hoist developer/system messages before conversation starts to systemInstruction, keeping token 0 cache prefix immutable. Demote mid-session developer messages to user role and merge consecutive user turns via MergeAdjacentGeminiUserContents without crossing functionResponse boundaries.
- Tool Call Buffer: buffer mid-session developer messages during pending function calls and emit after functionResponse, preserving valid tool pairing and reason replay.
- Chat Completions API: apply the same leading/mid-session distinction for system/developer messages and guard against empty parts.
- Claude Messages API: align role: "developer" in messages with role: "system", demoting to <system-reminder> user turns and avoiding upstream 400s.
- Validation: update ValidateGeminiFunctionCallPairing to allow intervening user turns before functionResponse, matching upstream Antigravity tolerance.
- Role Normalization: ensure functionResponse turns in Antigravity are always normalized to role: "user".
- Recursively inspect JSON schema properties to detect optional fields not listed in the `required` array.
- Downgrade `strict` to false when schemas contain optional properties to prevent backend HTTP 400 rejections.
Closes: #5463
- Prefix colliding tool names (`read_file`, `write_file`, `execute_code`) with `external_` when sending requests upstream to Antigravity models.
- Strip the `external_` prefix from Antigravity responses so clients receive the original tool names.
- Apply the name mapping across tool definitions, tool calls, tool results, and tool choice in both Chat Completions and Responses translators.
Closes: #5462
- Map Claude `max_tokens` stop reason to `response.incomplete` with `max_output_tokens` incomplete details.
- Propagate `incomplete` status to trailing output items across streaming and non-streaming responses.
- Defer and track output item finalization to ensure correct status and arguments when turns are truncated.
Closes: #5439
- Cache token usage metrics from streaming chunks instead of prematurely finalizing content blocks.
- Defer `message_delta` and `message_stop` emissions until encountering a finish reason, a trailing usage chunk, or stream completion.
Closes: #5419
- Emit provider-native signatures without model group prefixes in Claude responses.
- Validate client-provided signatures directly and restrict signature cache recovery to omitted signatures.
Closes: #4445
- Extract cache write tokens from OpenAI and Codex usage details.
- Map cache write tokens to Claude `cache_creation_input_tokens` for streaming and non-streaming responses.
Closes: #4262
- Preserve pending tool use IDs across message-level system reminders to maintain tool call and result alignment.
- Merge adjacent Gemini request contents for consecutive user and system reminder turns.
- Relax tool response reordering condition and preserve non-response parts in Antigravity executor.
- Align tool results and buffer pending system reminders in OpenAI translator.
Closes: #5354
- Strip trailing `thinking` and `redacted_thinking` blocks from the final assistant message during OpenAI responses translation.
- Drop the assistant message if no content blocks remain after stripping trailing thinking blocks.
Closes: #5321
- Strip trailing assistant prefill messages for Opus 5 and Sonnet 4.6 model families in addition to Fable models during OpenAI responses translation.
Closes: #5319
- Replace consecutive thinking blocks with the latest reasoning item in assistant messages.
- Flush pending tool use parts upon encountering subsequent reasoning items to maintain proper block separation.
Closes: #5317
- Fall back to array index in tool calls accumulator when the `index` field is missing.
- Ensure parallel tool calls without explicit indices are accumulated properly during streaming.
Closes: #5058
- Ensure `message_delta` is emitted on tool call usage chunks and stream completion even if `finish_reason` is missing or null.
- Add `terminalOpenAIFinishReason` fallback to map missing finish reasons to standard stop reasons.
- Consolidate content block finalization into `finalizeOpenAIAnthropicContentBlocks` and delta emission into `emitAnthropicMessageDelta`.
Closes: #5308
- Map Claude `output_config.format` with `json_schema` type to Codex `text.format`.
- Preserve custom schema name and strict configuration with appropriate defaults.
Closes: #5280
- Drop unsupported trailing assistant prefill messages when converting requests for Claude Fable models.
- Fall back to an empty user message turn when dropping assistant messages leaves the request empty.
Closes: #5279
- Cache trailing thought signatures associated with preceding text blocks via best-effort cache.
- Suppress emitting detached thinking carrier blocks after visible text in streaming and non-streaming responses.
Closes: #5272
- Deduct `cachedContentTokenCount` from `promptTokenCount` for `usage.input_tokens`.
- Set `usage.cache_read_input_tokens` when cached tokens are present in streaming and non-streaming responses.
Closes: #5238
- Map Claude `server_tool_use` and `web_search_tool_result` content blocks to OpenAI Responses `web_search_call` items in streaming and non-streaming modes.
- Support replaying `web_search_call` items and text search annotations back to Claude server tool blocks and citations.
Closes: #5236
- Track tool call indices independently using a sequential counter instead of reusing Claude content block indices.
- Set the sequential tool call index when emitting streaming delta chunks.
Closes: #5229
The Antigravity backend sometimes ends a 200 stream without ever emitting
finishReason. Evidence from local request logs: 25 of 18,346 captured
cloudcode-pa streams have no finishReason at all (gemini-3.7-flash x23,
gemini-3.6-flash x2). Gemini and OpenAI chat clients then never see a
terminal event and wait forever.
Only synthesize on a clean end of stream
- The [DONE] tail is now translated only when scanner.Err() is nil. A
truncated upstream stream previously still produced a terminal event:
replaying a cut stream to a Claude client emitted the full
content_block_stop / message_delta / message_stop sequence, so the
truncation was reported as a completed message.
- Only Antigravity translators synthesize on [DONE], so the other
executors that emit the tail before checking scanner.Err() cannot leak a
fake terminal event and are left unchanged.
Never finalize a stream that produced nothing
- Synthesis requires at least one chunk carrying candidates or token
accounting. Both translators share the same check, and presence alone is
not enough: `{}`, `{"response":{}}` and `{"response":{"candidates":[]}}`
leave the stream unstarted.
- Without that guard the synthetic chunk defeats the existing empty_stream
detection in sdk/cliproxy/auth/conductor_stream.go, which only fires when
the executor produced no chunk at all. An empty 200 would be reported as
a successful empty completion instead of a failure.
Synthetic chunks mirror the observed upstream shape
- All 18,321 real terminal chunks carry candidates/usageMetadata/
modelVersion/responseId with a model-role candidate whose parts are
[{"text":""}]. The Gemini synthetic chunk now reproduces that shape and
key order instead of a bare finishReason candidate.
- The last known usage snapshot is carried into the synthetic chunk.
Without it the final chunk a client sees reports no tokens, because
FilterSSEUsageMetadata renames non-terminal usage to cpaUsageMetadata
and the Gemini path restores it per chunk.
- The OpenAI chat path keeps the latest cpaUsageMetadata as pending usage
and emits it on [DONE] for the same reason.
Do not mistake an intermediate chunk for the terminal one
- A chunk carrying usage but no finishReason stays non-terminal.
FilterSSEUsageMetadata forwards real usageMetadata on such a chunk only
after an earlier chunk already carried finishReason, which the existing
condition covers; finalizing on usage alone would cut the stream short.
- finish_reason and native_finish_reason are resolved by one shared
helper, so the upstream terminal chunk and the synthesized [DONE] chunk
cannot drift apart.
- The non-stream Gemini conversion defaults a missing finishReason for
every candidate rather than only the first one.
Also fixes the unreachable alt != "" branch, which parsed an always-nil
buffer, and replaces an unchecked param type assertion.
Verified by replaying byte-exact upstream bodies extracted from request
logs through a mock backend, comparing this change against the unmodified
branch point: clean streams keep exactly one terminal event, streams
without finishReason gain one carrying the last usage snapshot, a stream
cut mid-chunk surfaces the read error with no terminal event, and an empty
200 now fails with empty_stream instead of reporting a successful empty
completion.
- Add `AlignClaudeToolResults` to order `tool_result` blocks to match the preceding `tool_use` IDs while preserving other content parts.
- Apply tool result alignment in Claude-to-Gemini and Claude-to-Antigravity request translators.
- Preserve mixed non-response parts when normalizing and reordering parallel function responses in Antigravity executor.
Closes: #5199
- Keep `functionResponse.response.result` as a string in Gemini responses translation instead of JSON-parsing tool output.
- Apply the same string-preserving behavior for antigravity tool responses to avoid upstream 400 errors from parsed payloads.
- Add `max_completion_tokens` fallback handling in OpenAI→Antigravity request conversion.
- Keep `max_tokens` as the preferred source when both fields are present.
Closes: #5108
- Add shared tool descriptor collection and winner selection for Responses tools (top-level vs `additional_tools`, direct vs namespace child, and ordering rules).
- Introduce sanitized Gemini function name mapping with collision disambiguation and 64-char-safe truncation.
- Build forward/reverse tool identity maps for restoring original tool identity (`name`, `namespace`, `custom`) during translation.
- Update Gemini→Responses streaming conversion to emit proper custom tool call events and identity-aware function call events.
- Add helpers for translating `tool_choice` to Gemini config and unwrapping custom tool input payloads.
Closes: #5088
Responses→Gemini emits functionResponse plus sibling inline_data, but
fixCLIToolResponse previously dropped those images when regrouping tool
turns. Cloud Code Assist only sees tool images nested in
functionResponse.parts with an explicit mimeType, and parallel tool
results must stay bound to the nearest preceding functionResponse
instead of last-wins.
Closes#5070
- Prefer `max_tokens` when both `max_tokens` and `max_completion_tokens` are present, otherwise use whichever exists.
- Default to existing template `max_tokens` limit when neither field is provided.
- Add regression coverage for all token-limit source/preference paths.
Closes: #5040
- Use original `call_id` / `tool_call_id` values as keys for tool-result/message caches and emitted-id tracking in Claude OpenAI request translators.
- Avoid mixed sanitized-vs-raw ID lookups that could cause duplicate or missed deduplication of repeated tool outputs.