fix: preserve Gemini thought_signature across multi-turn tool calls#33
Merged
Conversation
When using Gemini 2.5 thinking models (gemini-2.5-flash, gemini-2.5-pro)
with tools, the API returns thought parts containing a `thoughtSignature`.
This opaque token must be re-sent in subsequent conversation turns
alongside the functionCall parts, otherwise the API rejects the request
with HTTP 400 "missing thought_signature".
Changes in v2/src/core/agent-loop.mjs:
1. convertGoogleResponse(): handle `part.thought` without requiring
`part.text` (signatures can arrive on thought-only parts), and
preserve `part.thoughtSignature` as `_googleThoughtSignature` on
the thinking block.
2. streamGoogleResponse()/yieldChunkEvents(): branch on `part.thought`
first (not `part.text`) to correctly emit thinking_delta events even
when text is absent, and carry `part.thoughtSignature` on the event.
3. accumulateGoogleStream(): collect the thought signature from any
thinking_delta event and store it on the emitted thinking block.
Also create a thinking block when only a signature is present (no
text content).
4. callGoogle(): when building the `contents` array for the next API
call, re-emit thought parts (`{thought:true, thoughtSignature:...}`)
for any thinking block that has `_googleThoughtSignature`, restoring
the context the API requires.
Agent-Logs-Url: https://github.com/codomium/FreeCode/sessions/15d811e7-7624-4750-8a44-9514f6919c64
Co-authored-by: codomium <255525663+codomium@users.noreply.github.com>
Agent-Logs-Url: https://github.com/codomium/FreeCode/sessions/15d811e7-7624-4750-8a44-9514f6919c64 Co-authored-by: codomium <255525663+codomium@users.noreply.github.com>
Copilot created this pull request from a session on behalf of
codomium
May 12, 2026 20:39
View session
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.
Gemini 2.5 thinking models return a
thoughtSignatureon thought parts that must be echoed back verbatim in every subsequent conversation turn. The agent loop was silently dropping it, causing HTTP 400INVALID_ARGUMENTon any multi-turn tool use withgemini-2.5-flash/gemini-2.5-pro.Changes
convertGoogleResponse()— preservepart.thoughtSignature→_googleThoughtSignatureon the internal thinking block; also handlepart.thoughtparts that have notext(signature-only parts are valid)streamGoogleResponse()/yieldChunkEvents()— branch onpart.thought(notpart.text) first; forwardthoughtSignatureon emittedthinking_deltaeventsaccumulateGoogleStream()— collect signature from any streaming chunk and store it on the thinking block; create a thinking block even whenthinkingContentis empty but a signature is presentcallGoogle()request builder — re-emit{ thought: true, thoughtSignature: ... }parts beforefunctionCallparts when a thinking block carries_googleThoughtSignatureThe internal field name uses the underscore-prefix convention to distinguish it from the wire-format field
thoughtSignature.