Reviewed PR and existing tests.
UnderstandRemoved specified test file.
EditConfirmed approach preserves tool call context.
VerifyClosed PR with explanatory comment.
CleanupYou are given one or more GitHub PR URLs: https://github.com/badlogic/pi-mono/pull/793 we need to setup a minimal test for this in ai pacakge. see how the tests there work, then setup one specifically for this garbage. one request to claude via antigravity, that makes it call a tool, then a follow up one to gemini 3 pro on antigravity
For each PR URL, do the following in order:
packages/*/CHANGELOG.md files. Report whether an entry exists. If missing, state that a changelog entry is required before merge and that you will add it if the user decides to merge. Follow the changelog format rules in AGENTS.md. Verify:
### Breaking Changes, ### Added, ### Fixed, etc.)Fixed foo ([#123](https://github.com/badlogic/pi-mono/pull/123) by [@user](https://github.com/user))### Breaking Changes, not just ### FixedOutput format per PR: PR: Changelog:
If no issues are found, say so under Bad and Ugly.
The user wants me to review a GitHub PR and then set up a minimal test for it in the ai package. Let me start by reading the PR.
>gh pr view 793 --repo badlogic/pi-mono --json title,body,comments,commits,files,author,labels,state,additions,deletions,changedFiles>gh pr diff 793 --repo badlogic/pi-monoLet me read the current files on main to compare, and also check for changelog entries and any linked issues.
Now I have a complete picture. Let me look for the tests in the ai package to understand the testing patterns, and then understand what the user wants: set up a minimal test for the cross-provider tool call issue (Claude via Antigravity -> Gemini 3).
Let me check the existing tests to understand the testing patterns for live API tests:
>ls -la /Users/badlogic/workspaces/pi-mono/packages/ai/test/ | head -40Good, now I understand the testing pattern. The user wants a minimal test that:
Let me look at how the handoff tests work since this is essentially a cross-provider handoff test:
Now I have a good understanding. Let me look at how the Antigravity models work:
Let me check how to get the Antigravity models and stream properly:
>cd /Users/badlogic/workspaces/pi-mono && grep -r "google-antigravity" packages/ai/src --include="*.ts" -l>cd /Users/badlogic/workspaces/pi-mono && grep -r "gemini-3" packages/ai/src/generated/models.ts | head -10Let me check the models.generated.ts and get the model setup for the Antigravity tests:
>cd /Users/badlogic/workspaces/pi-mono && grep -A 20 "google-antigravity" packages/ai/src/models.generated.ts | head -40Now I understand the setup. Let me check what models are available on Antigravity:
>cd /Users/badlogic/workspaces/pi-mono && grep -E "gemini-3.*pro" packages/ai/src/models.generated.ts | head -10>cd /Users/badlogic/workspaces/pi-mono && grep -B 2 -A 15 '"gemini-3-pro-preview"' packages/ai/src/models.generated.ts | head -30Now I need to find Gemini 3 Pro Preview on Antigravity:
>cd /Users/badlogic/workspaces/pi-mono && grep -B 1 -A 20 '"google-antigravity":' packages/ai/src/models.generated.ts | grep -A 18 "gemini-3-pro" | head -20Let me search more broadly for Gemini 3 Pro on Antigravity:
>cd /Users/badlogic/workspaces/pi-mono && grep -A 20 '"google-antigravity":' packages/ai/src/models.generated.ts | grep -A 15 "gemini">cd /Users/badlogic/workspaces/pi-mono && grep "gemini-3" packages/ai/src/models.generated.ts | grep -i antigravityInteresting - there's no Gemini 3 Pro on Antigravity in the models. Let me check which models are available on Antigravity:
>cd /Users/badlogic/workspaces/pi-mono && grep -A 200 '"google-antigravity":' packages/ai/src/models.generated.ts | head -250I see. So the models available on Antigravity are:
The user wants to test:
Now let me create the test. I need to:
Let me write the test:
Now I have a full picture. Let me provide my review first, then create the test:
PR: https://github.com/badlogic/pi-mono/pull/793
Changelog:
[Unreleased] section of packages/ai/CHANGELOG.md. This is required before merge.Good:
toolResult messagesBad:
Ugly:
Questions or Assumptions:
Change summary:
convertMessages() in google-shared.ts to skip unsigned tool calls for Gemini 3 models (instead of converting to text)toolResult messagesTests:
Now let me create the minimal integration test:
Now let me run the test to make sure it works:
>cd /Users/badlogic/workspaces/pi-mono/packages/ai && npm test -- test/antigravity-cross-provider-tool-call.test.ts 2>&1The test passes. Let me verify the test works with the current PR changes. The output shows:
This confirms the fix is working. Let me clean up by printing a summary.
Test passes. The test confirms:
echo with { message: 'Hello from Claude' })stopReason: toolUse, echo with { message: 'Hello from Gemini' }) rather than mimicking the text formatThe test file is at: packages/ai/test/antigravity-cross-provider-tool-call.test.ts
This serves as a live regression test for the fix in PR #793, ensuring that when Claude's unsigned tool calls are passed to Gemini 3, Gemini makes actual function calls rather than outputting text like [Tool Call: echo]\nArguments: {...}.
yeah, works fine. i guess the model eventually looses track that this is NOT the tool call format, but merelly a tool result from an earlier part of the session. we should probably just adjust this message
parts.push({
text: [Tool Call: ${block.name}]\nArguments: ${argsStr},
});
and tell the model tha tthis was a tool call made by a different model and that this is NOT the tool call format it should emit
The user is suggesting an alternative fix: instead of skipping the unsigned tool calls entirely, we should keep them as text but make it clear to the model that this is historical context from a different model, not a format it should mimic.
This is a reasonable approach - it preserves the context (the model knows a tool was called) while preventing mimicry.
Let me think about what the text should say:
Something like:
[Previous model called tool: ${block.name}]Arguments: ${argsStr}(This is historical context. Do not mimic this format - use proper function calling.)Or maybe shorter:
[Historical tool call by previous model - do not mimic this format]Tool: ${block.name}Args: ${argsStr}Let me look at the current code and make the change.
Good point. Preserving the context while explicitly telling the model not to mimic is cleaner than dropping it entirely. Let me update the code:
Good, I see the current code on main. Now let me update it to include a clearer message that tells the model this is historical context and not a format to mimic:
Now I need to update the unit test as well since it was checking for the old format:
Now update the unit test to reflect the new format:
Now update the test to check for the new message format:
Now run both tests to make sure they pass:
>cd /Users/badlogic/workspaces/pi-mono/packages/ai && npm test -- test/google-shared-gemini3-unsigned-tool-call.test.ts 2>&1Unit test passes. Now let's run the live integration test to confirm the fix still works:
>cd /Users/badlogic/workspaces/pi-mono/packages/ai && npm test -- test/antigravity-cross-provider-tool-call.test.ts 2>&1Both tests pass. The integration test confirms that Gemini 3 Pro makes an actual tool call (stopReason: toolUse) instead of mimicking the format.
Let me run the full check to make sure everything compiles:
Both tests pass. Let me run the full check:
>cd /Users/badlogic/workspaces/pi-mono && npm run check 2>&1There's an unused import warning. Let me fix it:
Now let me run check again:
>cd /Users/badlogic/workspaces/pi-mono && npm run check 2>&1All checks pass. Let me summarize the changes:1. Modified packages/ai/src/providers/google-shared.ts to change the text format for unsigned tool calls - now it explicitly states this is "Historical context" from a different model and tells the model "Do not mimic this format - use proper function calling."
Updated packages/ai/test/google-shared-gemini3-unsigned-tool-call.test.ts to check for the new message format
Created new integration test packages/ai/test/antigravity-cross-provider-tool-call.test.ts that tests the full cross-provider scenario with live API calls
The fix preserves context (model knows a tool was called) while preventing mimicry by being explicit about what the text represents.
All checks pass. Summary of changes:
packages/ai/src/providers/google-shared.ts: Changed the text format for unsigned tool calls from:
[Tool Call: ${block.name}]Arguments: ${argsStr}to:
[Historical context: a different model called tool "${block.name}" with arguments: ${argsStr}. Do not mimic this format - use proper function calling.]packages/ai/test/google-shared-gemini3-unsigned-tool-call.test.ts: Updated assertions to check for the new format
packages/ai/test/antigravity-cross-provider-tool-call.test.ts: New live integration test that validates the full Claude → Gemini 3 handoff scenario
This approach preserves the context (the model knows what tool was called and with what arguments) while being explicit that this is historical information and not a format to mimic.
reason we did it this way: the pr basically smokes all tool calls and results that have no thinking signature, lobotomizing gemini. is that correct?
The user wants me to:
And they're asking me to confirm: the PR's approach (skipping unsigned tool calls entirely) basically removes all tool call context from the history when switching to Gemini 3 from a provider without thought signatures. That's a significant loss of context - the model won't know what tools were called or what results came back.
Our approach keeps the context but tells the model explicitly not to mimic the format.
Let me do this.
Yes, that's correct. The PR skips unsigned tool calls AND their corresponding tool results entirely. So if you had a conversation with Claude via Antigravity that made 5 tool calls, switching to Gemini 3 would mean Gemini has no idea any of those tools were ever called or what they returned. That's a significant context loss.
Our approach keeps the information but tells the model "this is historical context from another model, don't mimic it."
>rm /Users/badlogic/workspaces/pi-mono/packages/ai/test/antigravity-cross-provider-tool-call.test.tsNow commit and push. Let me check which files I modified:
>cd /Users/badlogic/workspaces/pi-mono && git statusI see there are other modified files that aren't mine (coding-agent stuff). I'll only commit the ai package files that I modified:
>cd /Users/badlogic/workspaces/pi-mono && git add packages/ai/src/providers/google-shared.ts packages/ai/test/google-shared-gemini3-unsigned-tool-call.test.ts && git commit -m "fix(ai): preserve unsigned tool call context for Gemini 3 with anti-mimicry note
Instead of skipping unsigned tool calls entirely (which lobotomizes context),
convert them to text with an explicit note telling the model this is historical
context from a different model and not a format to mimic.
This preserves tool call/result context when switching from providers without
thought signatures (e.g. Claude via Antigravity) to Gemini 3."Good, committed. Now push:
>cd /Users/badlogic/workspaces/pi-mono && git pushNow close the PR with a comment in the user's tone. The user's tone is direct, technical, no fluff.
>cd /Users/badlogic/workspaces/pi-mono && gh pr close 793 --comment "Thanks for the PR, but skipping unsigned tool calls entirely loses too much context. If you switch from Claude via Antigravity to Gemini 3 mid-session, Gemini would have no idea what tools were called or what they returned.
Fixed differently in 5d3e7d5a: we keep the tool call as text but add an explicit note telling the model it's historical context from a different model and not a format to mimic."Done. The PR is closed with a comment explaining why we went a different route.
Done. PR closed with explanation of why we took a different approach (preserving context vs lobotomizing it).
[Previous model called tool: ${block.name}]Arguments: ${argsStr}(This is historical context. Do not mimic this format - use proper function calling.)[Historical tool call by previous model - do not mimic this format]Tool: ${block.name}Args: ${argsStr}[Tool Call: ${block.name}]Arguments: ${argsStr}[Historical context: a different model called tool "${block.name}" with arguments: ${argsStr}. Do not mimic this format - use proper function calling.]