diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 570ff6cd5..0f794386d 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -600,21 +600,24 @@ func (al *AgentLoop) runLLMIteration(ctx context.Context, agent *AgentInstance, } for _, tc := range normalizedToolCalls { argumentsJSON, _ := json.Marshal(tc.Arguments) + // Copy ExtraContent to ensure thought_signature is persisted for Gemini 3 + extraContent := tc.ExtraContent thoughtSignature := "" if tc.Function != nil { thoughtSignature = tc.Function.ThoughtSignature } assistantMsg.ToolCalls = append(assistantMsg.ToolCalls, providers.ToolCall{ - ID: tc.ID, - Type: "function", - Name: tc.Name, - Arguments: tc.Arguments, + ID: tc.ID, + Type: "function", + Name: tc.Name, Function: &providers.FunctionCall{ Name: tc.Name, Arguments: string(argumentsJSON), ThoughtSignature: thoughtSignature, }, + ExtraContent: extraContent, + ThoughtSignature: thoughtSignature, }) } messages = append(messages, assistantMsg) diff --git a/pkg/providers/openai_compat/provider.go b/pkg/providers/openai_compat/provider.go index d894d98ce..6bc43a470 100644 --- a/pkg/providers/openai_compat/provider.go +++ b/pkg/providers/openai_compat/provider.go @@ -22,6 +22,8 @@ type UsageInfo = protocoltypes.UsageInfo type Message = protocoltypes.Message type ToolDefinition = protocoltypes.ToolDefinition type ToolFunctionDefinition = protocoltypes.ToolFunctionDefinition +type ExtraContent = protocoltypes.ExtraContent +type GoogleExtra = protocoltypes.GoogleExtra type Provider struct { apiKey string @@ -145,6 +147,11 @@ func parseResponse(body []byte) (*LLMResponse, error) { Name string `json:"name"` Arguments string `json:"arguments"` } `json:"function"` + ExtraContent *struct { + Google *struct { + ThoughtSignature string `json:"thought_signature"` + } `json:"google"` + } `json:"extra_content"` } `json:"tool_calls"` } `json:"message"` FinishReason string `json:"finish_reason"` @@ -169,6 +176,12 @@ func parseResponse(body []byte) (*LLMResponse, error) { arguments := make(map[string]interface{}) name := "" + // Extract thought_signature from Gemini/Google-specific extra content + thoughtSignature := "" + if tc.ExtraContent != nil && tc.ExtraContent.Google != nil { + thoughtSignature = tc.ExtraContent.Google.ThoughtSignature + } + if tc.Function != nil { name = tc.Function.Name if tc.Function.Arguments != "" { @@ -179,11 +192,23 @@ func parseResponse(body []byte) (*LLMResponse, error) { } } - toolCalls = append(toolCalls, ToolCall{ - ID: tc.ID, - Name: name, - Arguments: arguments, - }) + // Build ToolCall with ExtraContent for Gemini 3 thought_signature persistence + toolCall := ToolCall{ + ID: tc.ID, + Name: name, + Arguments: arguments, + ThoughtSignature: thoughtSignature, + } + + if thoughtSignature != "" { + toolCall.ExtraContent = &ExtraContent{ + Google: &GoogleExtra{ + ThoughtSignature: thoughtSignature, + }, + } + } + + toolCalls = append(toolCalls, toolCall) } return &LLMResponse{ diff --git a/pkg/providers/protocoltypes/types.go b/pkg/providers/protocoltypes/types.go index 53ebaee53..b7e7062b9 100644 --- a/pkg/providers/protocoltypes/types.go +++ b/pkg/providers/protocoltypes/types.go @@ -1,11 +1,21 @@ package protocoltypes type ToolCall struct { - ID string `json:"id"` - Type string `json:"type,omitempty"` - Function *FunctionCall `json:"function,omitempty"` - Name string `json:"name,omitempty"` - Arguments map[string]interface{} `json:"arguments,omitempty"` + ID string `json:"id"` + Type string `json:"type,omitempty"` + Function *FunctionCall `json:"function,omitempty"` + Name string `json:"name,omitempty"` + Arguments map[string]interface{} `json:"arguments,omitempty"` + ThoughtSignature string `json:"-"` // Internal use only + ExtraContent *ExtraContent `json:"extra_content,omitempty"` +} + +type ExtraContent struct { + Google *GoogleExtra `json:"google,omitempty"` +} + +type GoogleExtra struct { + ThoughtSignature string `json:"thought_signature,omitempty"` } type FunctionCall struct { diff --git a/pkg/providers/types.go b/pkg/providers/types.go index c4a9de58a..e783e6348 100644 --- a/pkg/providers/types.go +++ b/pkg/providers/types.go @@ -14,6 +14,8 @@ type UsageInfo = protocoltypes.UsageInfo type Message = protocoltypes.Message type ToolDefinition = protocoltypes.ToolDefinition type ToolFunctionDefinition = protocoltypes.ToolFunctionDefinition +type ExtraContent = protocoltypes.ExtraContent +type GoogleExtra = protocoltypes.GoogleExtra type LLMProvider interface { Chat(ctx context.Context, messages []Message, tools []ToolDefinition, model string, options map[string]interface{}) (*LLMResponse, error)