Skip to content

fix: preserve extra_content for Gemini 3 thought_signature support#5267

Merged
kevinvandijk merged 3 commits intoKilo-Org:mainfrom
maywzh:fix/gemini3-extra-content-preservation
Feb 20, 2026
Merged

fix: preserve extra_content for Gemini 3 thought_signature support#5267
kevinvandijk merged 3 commits intoKilo-Org:mainfrom
maywzh:fix/gemini3-extra-content-preservation

Conversation

@maywzh
Copy link
Copy Markdown

@maywzh maywzh commented Jan 21, 2026

Context

This PR adds support for preserving extra_content (including Gemini 3's thought_signature) throughout the tool call lifecycle.

Gemini 3 models include extra_content with thought_signature in tool call responses. This metadata was being lost during multi-turn conversations, potentially affecting model coherence and performance.

Implementation

  • Added extra_content field to ToolUse, McpToolUse, and streaming interfaces
  • Preserved extra_content in OpenAI and OpenRouter providers during streaming
  • Passed extra_content through message format conversion
  • Tracked and inherited extra_content during tool call streaming lifecycle in Task.ts

Changes:

  • src/api/providers/openai.ts - Extract and preserve extra_content
  • src/api/providers/openrouter.ts - Extract and preserve extra_content
  • src/api/transform/openai-format.ts - Include extra_content in conversion
  • src/api/transform/stream.ts - Add extra_content to streaming interfaces
  • src/core/assistant-message/NativeToolCallParser.ts - Parse and track extra_content
  • src/core/task/Task.ts - Preserve extra_content in tool call event handling
  • src/shared/tools.ts - Add extra_content to ToolUse and McpToolUse interfaces

Backward Compatibility:
All changes are backward compatible:

  • extra_content is optional (?)
  • Existing providers without extra_content continue to work unchanged

Screenshots

before after
N/A N/A

How to Test

  • Manual testing with Gemini 3 models via OpenRouter
  • Verified multi-turn conversations preserve thought signatures

Get in Touch

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jan 21, 2026

🦋 Changeset detected

Latest commit: 543050c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
kilo-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@maywzh maywzh force-pushed the fix/gemini3-extra-content-preservation branch 11 times, most recently from 6fe7ba4 to ea3fb9d Compare January 26, 2026 11:11
@maywzh maywzh force-pushed the fix/gemini3-extra-content-preservation branch from ea3fb9d to 1467783 Compare January 30, 2026 05:44
name: toolCall.function?.name,
arguments: toolCall.function?.arguments,
// Preserve extra_content for Gemini 3 thought_signature support
extra_content: (toolCall as any).extra_content,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING]: Unvalidated as any cast on untrusted API response data

(toolCall as any).extra_content extracts an arbitrary property from the API stream response without any validation. This value flows through the entire pipeline and is eventually sent back to the API in subsequent requests.

Consider adding minimal validation (e.g., checking it's a plain object) to prevent unexpected data types from propagating:

const rawExtra = (toolCall as Record<string, unknown>).extra_content
extra_content: rawExtra != null && typeof rawExtra === 'object' && !Array.isArray(rawExtra)
  ? rawExtra as Record<string, unknown>
  : undefined,

Comment thread src/core/task/Task.ts
}
seenToolUseIds.add(sanitizedId)
assistantContent.push({
const toolUseBlock: any = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING]: Type safety regression — const toolUseBlock: any loses type checking

The previous code pushed a typed object literal directly into assistantContent (typed as Array<Anthropic.ContentBlockParam>). Changing to any disables all type checking on this object.

Consider using an intersection type instead to preserve type safety while allowing the extra property:

const toolUseBlock: Anthropic.ToolUseBlockParam & { extra_content?: Record<string, unknown> } = {
  type: "tool_use" as const,
  id: sanitizedId,
  name: mcpBlock.name,
  input: mcpBlock.arguments,
}

The same applies to the second toolUseBlock: any on line 3828.

@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot Bot commented Feb 20, 2026

Code Review Summary

Status: 3 Issues Found | Recommendation: Address before merge

Fix these issues in Kilo Cloud

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
src/api/providers/openai.ts 528 Unvalidated as any cast on untrusted API response to access extra_content
src/api/transform/openai-format.ts 521 Unvalidated as any cast on toolMessage (typed as Anthropic.ToolUseBlockParam) to access extra_content
src/core/task/Task.ts 3793 Type safety regression — toolUseBlock typed as any instead of a proper intersection type
Other Observations (not in diff)

Issues found in unchanged code or cross-cutting concerns that cannot receive inline comments:

File Line Issue
src/core/assistant-message/__tests__/NativeToolCallParser.spec.ts Missing test coverage for extra_content propagation. No tests verify that extra_content is stored in rawChunkTracker, emitted in tool_call_start events, preserved through startStreamingToolCallfinalizeStreamingToolCall, or included in the final ToolUse/McpToolUse from parseToolCall/parseDynamicMcpTool. Per project guidelines, code changes should have test coverage.
src/api/providers/gemini.ts 325 The native Gemini provider does not pass extra_content through tool_call_partial chunks. If Gemini 3 thought_signature is also exposed through the native Gemini SDK (not just OpenAI-compatible endpoints), this would be a gap.
src/core/task/Task.ts 3160, 3189 Additional as any casts on existingToolUse in the delta and end event handlers follow the same pattern flagged at line 3793. Consider defining a shared type for content blocks that includes extra_content.
Files Reviewed (7 files)
  • src/api/providers/openai.ts - 1 issue (existing)
  • src/api/transform/openai-format.ts - 1 issue
  • src/api/transform/stream.ts - 0 issues
  • src/core/assistant-message/NativeToolCallParser.ts - 0 issues (test gap noted in observations)
  • src/core/task/Task.ts - 1 issue (existing) + observations
  • src/shared/tools.ts - 0 issues
  • .changeset/gemini-extra-content.md - 0 issues

},
}
// Preserve extra_content for Gemini 3 thought_signature support
const toolMessageWithExtra = toolMessage as any
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING]: Unvalidated as any cast on toolMessage to access extra_content

Same pattern as in openai.ts:528. The toolMessage is typed as Anthropic.ToolUseBlockParam which doesn't include extra_content. Consider adding a type guard or extending the type with an intersection to avoid silently accepting any shape:

interface ToolUseBlockWithExtra extends Anthropic.ToolUseBlockParam {
  extra_content?: Record<string, unknown>
}
const toolMessageWithExtra = toolMessage as ToolUseBlockWithExtra

This would provide compile-time safety while still allowing the extra field.

Copy link
Copy Markdown
Contributor

@kevinvandijk kevinvandijk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants