Skip to content

fix(agent-core-v2): make Gemini tool call ids unique across turns#1730

Merged
sailist merged 2 commits into
mainfrom
fix/gemini-tool-call-id-collision
Jul 15, 2026
Merged

fix(agent-core-v2): make Gemini tool call ids unique across turns#1730
sailist merged 2 commits into
mainfrom
fix/gemini-tool-call-id-collision

Conversation

@wbxl2000

Copy link
Copy Markdown
Collaborator

Related Issue

No issue — found while investigating a duplicated swarm card in the web UI.

Problem

When a Gemini-protocol backend issues small per-response function-call ids (e.g. 0), the provider synthesized tool call ids as ${name}_${id}. The sequence restarts every turn, so two AgentSwarm calls in different turns both became AgentSwarm_0. Tool call ids are the key for tool rows and swarm-member grouping in the web client, so the second card rendered both generations of members — 10 completed + 10 running, 10 / 20 instead of 10 / 10 (reproduced from a real session's event journal: 20 subagent.spawned events across two turns, all with parentToolCallId: "AgentSwarm_0").

What changed

  • google-genai.ts (both the agent-core-v2 and kosong copies): append an 8-hex-char entropy suffix to the synthesized id (${name}_${upstreamId}_${rand}), keeping the upstream id for readability while making ids unique across the whole session. The upstream id was never round-tripped (outbound function calls serialize only the name), so this is behavior-preserving otherwise.
  • Updated three kosong test expectations to match the new id shape.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works. (updated existing expectations; kosong 1214/1214, agent-core-v2 llmProtocol 159/159)
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

@changeset-bot

changeset-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e1b7a39

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

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-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

@pkg-pr-new

pkg-pr-new Bot commented Jul 15, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@e1b7a39
npx https://pkg.pr.new/@moonshot-ai/kimi-code@e1b7a39

commit: e1b7a39

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 00066c7445

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// stay unique across the whole session.
const id_ = (fc['id'] as string) ?? crypto.randomUUID();
const toolCallId = `${name}_${id_}`;
const toolCallId = `${name}_${id_}_${crypto.randomUUID().replaceAll('-', '').slice(0, 8)}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve the fallback tool-name parser

When the preceding assistant tool call has been compacted away and a standalone tool result reaches messagesToGoogleGenAIContents, toolCallIdToName is the only thing that recovers the Gemini functionResponse.name. This new ${name}_${upstreamId}_${rand} shape is no longer parseable by that fallback because it strips only the final suffix, so an id like AgentSwarm_0_ab12cd34 is sent back as function name AgentSwarm_0 instead of AgentSwarm; please update the encoding/parser together with this id-shape change, including the mirrored kosong provider.

Useful? React with 👍 / 👎.

const fc = (p['functionCall'] ?? p['function_call']) as Record<string, unknown>;
const name = fc['name'] as string;
if (!name) continue;
// The upstream function-call id is only unique within its own

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Move this note out of the v2 implementation body

packages/agent-core-v2/AGENTS.md requires comments to live only in the top-of-file /** */ block and says never to place them beside functions, methods, or statements. This newly added inline implementation note violates that package rule; please remove it or fold the necessary responsibility-level context into the file header.

Useful? React with 👍 / 👎.

The orphan tool-message fallback in toolCallIdToName stripped only one
trailing "_<suffix>" segment, so with the new
"{tool_name}_{upstream_id}_{entropy}" id shape it returned
"AgentSwarm_0" instead of "AgentSwarm" once the preceding assistant
call had been compacted away. Strip the fixed 8-hex entropy suffix
first, then the upstream id segment, in both the kosong and
agent-core-v2 providers. Also drop the inline implementation comment
that agent-core-v2 forbids outside the file header.
@sailist sailist merged commit 72f425e into main Jul 15, 2026
14 checks passed
@sailist sailist deleted the fix/gemini-tool-call-id-collision branch July 15, 2026 09:47
@github-actions github-actions Bot mentioned this pull request Jul 15, 2026
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