Skip to content

Commit 0cf33fd

Browse files
authored
fix(gemini-cli): align Cloud Code transport (diegosouzapw#1869)
Integrated into release/v3.7.9
1 parent 476ef48 commit 0cf33fd

12 files changed

Lines changed: 309 additions & 54 deletions

File tree

open-sse/executors/gemini-cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ export class GeminiCLIExecutor extends BaseExecutor {
120120
}
121121

122122
buildUrl(model, stream, urlIndex = 0) {
123+
void model;
124+
void urlIndex;
123125
const action = stream ? "streamGenerateContent?alt=sse" : "generateContent";
124126
return `${this.config.baseUrl}:${action}`;
125127
}

open-sse/handlers/responseTranslator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,12 @@ export function translateNonStreamingResponse(
281281
const fn = toRecord(partObj.functionCall);
282282
const rawName = toString(fn.name);
283283
const restoredName = toolNameMap?.get(rawName) ?? rawName;
284+
const nativeId = toString(fn.id);
284285
toolCalls.push({
285-
id: `call_${toString(restoredName, "unknown")}_${Date.now()}_${toolCalls.length}`,
286+
id:
287+
nativeId.length > 0
288+
? nativeId
289+
: `call_${toString(restoredName, "unknown")}_${Date.now()}_${toolCalls.length}`,
286290
type: "function",
287291
function: {
288292
name: restoredName,

open-sse/services/geminiCliHeaders.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ import {
77
export const GEMINI_CLI_VERSION = "0.40.1";
88
export const GEMINI_CLI_GOOGLE_API_NODE_CLIENT_VERSION = "9.15.1";
99

10+
const GEMINI_CLI_LOAD_CODE_ASSIST_METADATA = Object.freeze({
11+
ideType: "IDE_UNSPECIFIED",
12+
platform: "PLATFORM_UNSPECIFIED",
13+
pluginType: "GEMINI",
14+
});
15+
16+
export function getGeminiCliLoadCodeAssistMetadata(): Record<string, string> {
17+
return { ...GEMINI_CLI_LOAD_CODE_ASSIST_METADATA };
18+
}
19+
1020
export function geminiCliUserAgent(model: string): string {
1121
const normalizedModel = model || "unknown";
1222
return `GeminiCLI/${GEMINI_CLI_VERSION}/${normalizedModel} (${normalizeCloudCodePlatform()}; ${normalizeCloudCodeArch()}; terminal) google-api-nodejs-client/${GEMINI_CLI_GOOGLE_API_NODE_CLIENT_VERSION}`;

open-sse/translator/request/openai-to-gemini.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import {
2121
convertOpenAIContentToParts,
2222
extractTextContent,
2323
tryParseJSON,
24-
generateRequestId,
2524
generateSessionId,
2625
cleanJSONSchemaForAntigravity,
2726
} from "../helpers/geminiHelper.ts";
@@ -97,6 +96,7 @@ type CloudCodeEnvelope = {
9796

9897
type GeminiToolNameOptions = {
9998
stripNamespace?: boolean;
99+
functionResponseShape?: "result" | "output";
100100
};
101101

102102
function buildChangedToolNameMap(toolNameMap: Map<string, string>): Map<string, string> | null {
@@ -296,7 +296,10 @@ function openaiToGeminiBase(model, body, stream, toolNameOptions: GeminiToolName
296296
functionResponse: {
297297
id: fid,
298298
name: name,
299-
response: { result: parsedResp },
299+
response:
300+
toolNameOptions.functionResponseShape === "output"
301+
? { output: typeof resp === "string" ? resp : JSON.stringify(resp) }
302+
: { result: parsedResp },
300303
},
301304
});
302305
}
@@ -350,8 +353,16 @@ export function openaiToGeminiRequest(model, body, stream) {
350353
}
351354

352355
// OpenAI -> Gemini CLI (Cloud Code Assist)
353-
export function openaiToGeminiCLIRequest(model, body, stream) {
354-
const gemini = openaiToGeminiBase(model, body, stream, { stripNamespace: true });
356+
export function openaiToGeminiCLIRequest(
357+
model,
358+
body,
359+
stream,
360+
options: { functionResponseShape?: "result" | "output" } = {}
361+
) {
362+
const gemini = openaiToGeminiBase(model, body, stream, {
363+
stripNamespace: true,
364+
functionResponseShape: options.functionResponseShape,
365+
});
355366

356367
// Add thinking config for CLI
357368
if (body.reasoning_effort) {
@@ -413,7 +424,7 @@ function wrapInCloudCodeEnvelope(model, geminiCLI, credentials = null, isAntigra
413424
: {
414425
model: cleanModel,
415426
project: projectId,
416-
user_prompt_id: generateRequestId(),
427+
user_prompt_id: generateUUID(),
417428
request: {
418429
contents: geminiCLI.contents,
419430
systemInstruction: geminiCLI.systemInstruction,
@@ -443,7 +454,7 @@ function wrapInCloudCodeEnvelope(model, geminiCLI, credentials = null, isAntigra
443454
}
444455
} else {
445456
// Gemini CLI's native Cloud Code envelope uses snake_case identifiers.
446-
envelope.request.session_id = generateSessionId();
457+
envelope.request.session_id = envelope.user_prompt_id;
447458
envelope.request.safetySettings = geminiCLI.safetySettings;
448459
}
449460

@@ -619,7 +630,11 @@ register(
619630
FORMATS.OPENAI,
620631
FORMATS.GEMINI_CLI,
621632
(model, body, stream, credentials) =>
622-
wrapInCloudCodeEnvelope(model, openaiToGeminiCLIRequest(model, body, stream), credentials),
633+
wrapInCloudCodeEnvelope(
634+
model,
635+
openaiToGeminiCLIRequest(model, body, stream, { functionResponseShape: "output" }),
636+
credentials
637+
),
623638
null
624639
);
625640
register(FORMATS.OPENAI, FORMATS.ANTIGRAVITY, openaiToAntigravityRequest, null);

open-sse/translator/response/gemini-to-openai.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ import { register } from "../registry.ts";
22
import { FORMATS } from "../formats.ts";
33
import { storeGeminiThoughtSignature } from "../../services/geminiThoughtSignatureStore.ts";
44

5+
function buildToolCallId(functionCall, toolName, toolCallIndex) {
6+
return typeof functionCall?.id === "string" && functionCall.id.length > 0
7+
? functionCall.id
8+
: `${toolName}-${Date.now()}-${toolCallIndex}`;
9+
}
10+
511
// Convert Gemini response chunk to OpenAI format
612
export function geminiToOpenAIResponse(chunk, state) {
713
if (!chunk) return null;
@@ -111,7 +117,7 @@ export function geminiToOpenAIResponse(chunk, state) {
111117
const toolCallIndex = state.functionIndex++;
112118

113119
const toolCall = {
114-
id: `${fcName}-${Date.now()}-${toolCallIndex}`,
120+
id: buildToolCallId(part.functionCall, fcName, toolCallIndex),
115121
index: toolCallIndex,
116122
type: "function",
117123
function: {
@@ -169,7 +175,7 @@ export function geminiToOpenAIResponse(chunk, state) {
169175
const toolCallIndex = state.functionIndex++;
170176

171177
const toolCall = {
172-
id: `${fcName}-${Date.now()}-${toolCallIndex}`,
178+
id: buildToolCallId(part.functionCall, fcName, toolCallIndex),
173179
index: toolCallIndex,
174180
type: "function",
175181
function: {

src/lib/oauth/providers/gemini.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { GEMINI_CONFIG } from "../constants/oauth";
2+
import { getGeminiCliLoadCodeAssistMetadata } from "@omniroute/open-sse/services/geminiCliHeaders.ts";
23

34
export const gemini = {
45
config: GEMINI_CONFIG,
@@ -72,11 +73,7 @@ export const gemini = {
7273
"Content-Type": "application/json",
7374
},
7475
body: JSON.stringify({
75-
metadata: {
76-
ideType: "IDE_UNSPECIFIED",
77-
platform: "PLATFORM_UNSPECIFIED",
78-
pluginType: "GEMINI",
79-
},
76+
metadata: getGeminiCliLoadCodeAssistMetadata(),
8077
}),
8178
}
8279
);

src/lib/oauth/services/gemini.ts

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import crypto from "crypto";
22
import open from "open";
3-
import {
4-
ANTIGRAVITY_LOAD_CODE_ASSIST_API_CLIENT,
5-
ANTIGRAVITY_LOAD_CODE_ASSIST_USER_AGENT,
6-
} from "@omniroute/open-sse/services/antigravityHeaders.ts";
3+
import { getGeminiCliLoadCodeAssistMetadata } from "@omniroute/open-sse/services/geminiCliHeaders.ts";
74
import { GEMINI_CONFIG } from "../constants/oauth";
85
import { getServerCredentials } from "../config/index";
96
import { startLocalServer } from "../utils/server";
@@ -73,20 +70,9 @@ export class GeminiCLIService {
7370
headers: {
7471
Authorization: `Bearer ${accessToken}`,
7572
"Content-Type": "application/json",
76-
"User-Agent": ANTIGRAVITY_LOAD_CODE_ASSIST_USER_AGENT,
77-
"X-Goog-Api-Client": ANTIGRAVITY_LOAD_CODE_ASSIST_API_CLIENT,
78-
"Client-Metadata": JSON.stringify({
79-
ideType: "IDE_UNSPECIFIED",
80-
platform: "PLATFORM_UNSPECIFIED",
81-
pluginType: "GEMINI",
82-
}),
8373
},
8474
body: JSON.stringify({
85-
metadata: {
86-
ideType: "IDE_UNSPECIFIED",
87-
platform: "PLATFORM_UNSPECIFIED",
88-
pluginType: "GEMINI",
89-
},
75+
metadata: getGeminiCliLoadCodeAssistMetadata(),
9076
}),
9177
});
9278

src/shared/constants/providers.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@ export const FREE_PROVIDERS = {
1919
name: "Gemini CLI",
2020
icon: "terminal",
2121
color: "#4285F4",
22-
deprecated: true,
23-
deprecationReason:
24-
"Google restricts third-party OAuth usage for Gemini CLI (Mar 2026). Pro models require paid plans. Use 'gemini' (API key) provider instead.",
22+
authHint:
23+
"Uses Gemini CLI OAuth / Cloud Code credentials. Pro models require an eligible Google account or paid plan.",
2524
},
2625
kiro: { id: "kiro", alias: "kr", name: "Kiro AI", icon: "psychology_alt", color: "#FF6B35" },
2726
"amazon-q": {

0 commit comments

Comments
 (0)