Ver Fonte

Add Gemini CLI provider (#881)

* Add Gemini CLI provider

* Update changelog with proper credits
Kevin van Dijk há 7 meses atrás
pai
commit
30836f4d11
38 ficheiros alterados com 1203 adições e 0 exclusões
  1. 5 0
      .changeset/good-aliens-jog.md
  2. 8 0
      packages/types/src/provider-settings.ts
  3. 110 0
      packages/types/src/providers/gemini-cli.ts
  4. 1 0
      packages/types/src/providers/index.ts
  5. 3 0
      src/api/index.ts
  6. 414 0
      src/api/providers/gemini-cli.ts
  7. 1 0
      src/api/providers/index.ts
  8. 7 0
      webview-ui/src/components/settings/ApiOptions.tsx
  9. 3 0
      webview-ui/src/components/settings/constants.ts
  10. 81 0
      webview-ui/src/components/settings/providers/GeminiCli.tsx
  11. 169 0
      webview-ui/src/components/settings/providers/__tests__/GeminiCli.tsx
  12. 1 0
      webview-ui/src/components/settings/providers/index.ts
  13. 7 0
      webview-ui/src/components/ui/hooks/useSelectedModel.ts
  14. 15 0
      webview-ui/src/i18n/locales/ca/settings.json
  15. 30 0
      webview-ui/src/i18n/locales/cs/settings.json
  16. 30 0
      webview-ui/src/i18n/locales/de/settings.json
  17. 15 0
      webview-ui/src/i18n/locales/el/settings.json
  18. 15 0
      webview-ui/src/i18n/locales/en/settings.json
  19. 15 0
      webview-ui/src/i18n/locales/es/settings.json
  20. 15 0
      webview-ui/src/i18n/locales/fil/settings.json
  21. 15 0
      webview-ui/src/i18n/locales/fr/settings.json
  22. 15 0
      webview-ui/src/i18n/locales/hi/settings.json
  23. 15 0
      webview-ui/src/i18n/locales/id/settings.json
  24. 15 0
      webview-ui/src/i18n/locales/it/settings.json
  25. 15 0
      webview-ui/src/i18n/locales/ja/settings.json
  26. 15 0
      webview-ui/src/i18n/locales/ko/settings.json
  27. 15 0
      webview-ui/src/i18n/locales/nl/settings.json
  28. 15 0
      webview-ui/src/i18n/locales/pl/settings.json
  29. 15 0
      webview-ui/src/i18n/locales/pt-BR/settings.json
  30. 15 0
      webview-ui/src/i18n/locales/ru/settings.json
  31. 15 0
      webview-ui/src/i18n/locales/sv/settings.json
  32. 15 0
      webview-ui/src/i18n/locales/th/settings.json
  33. 15 0
      webview-ui/src/i18n/locales/tr/settings.json
  34. 15 0
      webview-ui/src/i18n/locales/uk/settings.json
  35. 15 0
      webview-ui/src/i18n/locales/vi/settings.json
  36. 15 0
      webview-ui/src/i18n/locales/zh-CN/settings.json
  37. 15 0
      webview-ui/src/i18n/locales/zh-TW/settings.json
  38. 3 0
      webview-ui/src/utils/validate.ts

+ 5 - 0
.changeset/good-aliens-jog.md

@@ -0,0 +1,5 @@
+---
+"kilo-code": minor
+---
+
+Add support for Gemini CLI provider (thanks Roo & Cline!)

+ 8 - 0
packages/types/src/provider-settings.ts

@@ -19,6 +19,7 @@ export const providerNames = [
 	"vscode-lm",
 	"lmstudio",
 	"gemini",
+	"gemini-cli",
 	"openai-native",
 	"mistral",
 	"deepseek",
@@ -168,6 +169,11 @@ const geminiSchema = apiModelIdProviderModelSchema.extend({
 	googleGeminiBaseUrl: z.string().optional(),
 })
 
+const geminiCliSchema = apiModelIdProviderModelSchema.extend({
+	geminiCliOAuthPath: z.string().optional(),
+	geminiCliProjectId: z.string().optional(),
+})
+
 const openAiNativeSchema = apiModelIdProviderModelSchema.extend({
 	openAiNativeApiKey: z.string().optional(),
 	openAiNativeBaseUrl: z.string().optional(),
@@ -250,6 +256,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
 	vsCodeLmSchema.merge(z.object({ apiProvider: z.literal("vscode-lm") })),
 	lmStudioSchema.merge(z.object({ apiProvider: z.literal("lmstudio") })),
 	geminiSchema.merge(z.object({ apiProvider: z.literal("gemini") })),
+	geminiCliSchema.merge(z.object({ apiProvider: z.literal("gemini-cli") })),
 	openAiNativeSchema.merge(z.object({ apiProvider: z.literal("openai-native") })),
 	mistralSchema.merge(z.object({ apiProvider: z.literal("mistral") })),
 	deepSeekSchema.merge(z.object({ apiProvider: z.literal("deepseek") })),
@@ -280,6 +287,7 @@ export const providerSettingsSchema = z.object({
 	...vsCodeLmSchema.shape,
 	...lmStudioSchema.shape,
 	...geminiSchema.shape,
+	...geminiCliSchema.shape,
 	...openAiNativeSchema.shape,
 	...mistralSchema.shape,
 	...deepSeekSchema.shape,

+ 110 - 0
packages/types/src/providers/gemini-cli.ts

@@ -0,0 +1,110 @@
+import type { ModelInfo } from "../model.js"
+
+// Gemini CLI models with free tier pricing (all $0)
+export type GeminiCliModelId = keyof typeof geminiCliModels
+
+export const geminiCliDefaultModelId: GeminiCliModelId = "gemini-2.0-flash-001"
+
+export const geminiCliModels = {
+	"gemini-2.0-flash-001": {
+		maxTokens: 8192,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-2.0-flash-thinking-exp-01-21": {
+		maxTokens: 65_536,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-2.0-flash-thinking-exp-1219": {
+		maxTokens: 8192,
+		contextWindow: 32_767,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-2.0-flash-exp": {
+		maxTokens: 8192,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-1.5-flash-002": {
+		maxTokens: 8192,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-1.5-flash-exp-0827": {
+		maxTokens: 8192,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-1.5-flash-8b-exp-0827": {
+		maxTokens: 8192,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-1.5-pro-002": {
+		maxTokens: 8192,
+		contextWindow: 2_097_152,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-1.5-pro-exp-0827": {
+		maxTokens: 8192,
+		contextWindow: 2_097_152,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-exp-1206": {
+		maxTokens: 8192,
+		contextWindow: 2_097_152,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-2.5-flash": {
+		maxTokens: 64_000,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		maxThinkingTokens: 24_576,
+		supportsReasoningBudget: true,
+	},
+	"gemini-2.5-pro": {
+		maxTokens: 64_000,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		maxThinkingTokens: 32_768,
+		supportsReasoningBudget: true,
+		requiredReasoningBudget: true,
+	},
+} as const satisfies Record<string, ModelInfo>

+ 1 - 0
packages/types/src/providers/index.ts

@@ -4,6 +4,7 @@ export * from "./chutes.js"
 export * from "./claude-code.js"
 export * from "./deepseek.js"
 export * from "./gemini.js"
+export * from "./gemini-cli.js"
 export * from "./glama.js"
 export * from "./groq.js"
 export * from "./lite-llm.js"

+ 3 - 0
src/api/index.ts

@@ -15,6 +15,7 @@ import {
 	OllamaHandler,
 	LmStudioHandler,
 	GeminiHandler,
+	GeminiCliHandler,
 	OpenAiNativeHandler,
 	DeepSeekHandler,
 	MistralHandler,
@@ -92,6 +93,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
 			return new LmStudioHandler(options)
 		case "gemini":
 			return new GeminiHandler(options)
+		case "gemini-cli":
+			return new GeminiCliHandler(options)
 		case "openai-native":
 			return new OpenAiNativeHandler(options)
 		case "deepseek":

+ 414 - 0
src/api/providers/gemini-cli.ts

@@ -0,0 +1,414 @@
+import type { Anthropic } from "@anthropic-ai/sdk"
+import { OAuth2Client } from "google-auth-library"
+import * as fs from "fs/promises"
+import * as path from "path"
+import * as os from "os"
+import axios from "axios"
+
+import { type ModelInfo, type GeminiCliModelId, geminiCliDefaultModelId, geminiCliModels } from "@roo-code/types"
+
+import type { ApiHandlerOptions } from "../../shared/api"
+
+import { convertAnthropicContentToGemini, convertAnthropicMessageToGemini } from "../transform/gemini-format"
+import type { ApiStream } from "../transform/stream"
+import { getModelParams } from "../transform/model-params"
+
+import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
+import { BaseProvider } from "./base-provider"
+
+// OAuth2 Configuration (from Cline implementation)
+const OAUTH_CLIENT_ID = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com"
+const OAUTH_CLIENT_SECRET = "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"
+const OAUTH_REDIRECT_URI = "http://localhost:45289"
+
+// Code Assist API Configuration
+const CODE_ASSIST_ENDPOINT = "https://cloudcode-pa.googleapis.com"
+const CODE_ASSIST_API_VERSION = "v1internal"
+
+interface OAuthCredentials {
+	access_token: string
+	refresh_token: string
+	token_type: string
+	expiry_date: number
+}
+
+export class GeminiCliHandler extends BaseProvider implements SingleCompletionHandler {
+	protected options: ApiHandlerOptions
+	private authClient: OAuth2Client
+	private projectId: string | null = null
+	private credentials: OAuthCredentials | null = null
+
+	constructor(options: ApiHandlerOptions) {
+		super()
+		this.options = options
+
+		// Initialize OAuth2 client
+		this.authClient = new OAuth2Client(OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_REDIRECT_URI)
+	}
+
+	private async loadOAuthCredentials(): Promise<void> {
+		try {
+			const credPath = this.options.geminiCliOAuthPath || path.join(os.homedir(), ".gemini", "oauth_creds.json")
+			const credData = await fs.readFile(credPath, "utf-8")
+			this.credentials = JSON.parse(credData)
+
+			// Set credentials on the OAuth2 client
+			if (this.credentials) {
+				this.authClient.setCredentials({
+					access_token: this.credentials.access_token,
+					refresh_token: this.credentials.refresh_token,
+					expiry_date: this.credentials.expiry_date,
+				})
+			}
+		} catch (error) {
+			throw new Error(`Failed to load OAuth credentials. Please authenticate first: ${error}`)
+		}
+	}
+
+	private async ensureAuthenticated(): Promise<void> {
+		if (!this.credentials) {
+			await this.loadOAuthCredentials()
+		}
+
+		// Check if token needs refresh
+		if (this.credentials && this.credentials.expiry_date < Date.now()) {
+			try {
+				const { credentials } = await this.authClient.refreshAccessToken()
+				if (credentials.access_token) {
+					this.credentials = {
+						access_token: credentials.access_token!,
+						refresh_token: credentials.refresh_token || this.credentials.refresh_token,
+						token_type: credentials.token_type || "Bearer",
+						expiry_date: credentials.expiry_date || Date.now() + 3600 * 1000,
+					}
+					// Optionally save refreshed credentials back to file
+					const credPath =
+						this.options.geminiCliOAuthPath || path.join(os.homedir(), ".gemini", "oauth_creds.json")
+					await fs.writeFile(credPath, JSON.stringify(this.credentials, null, 2))
+				}
+			} catch (error) {
+				throw new Error(`Failed to refresh OAuth token: ${error}`)
+			}
+		}
+	}
+
+	/**
+	 * Call a Code Assist API endpoint
+	 */
+	private async callEndpoint(method: string, body: any, retryAuth: boolean = true): Promise<any> {
+		console.log(`[GeminiCLI] Calling endpoint: ${method}`)
+		console.log(`[GeminiCLI] Request body:`, JSON.stringify(body, null, 2))
+
+		try {
+			const res = await this.authClient.request({
+				url: `${CODE_ASSIST_ENDPOINT}/${CODE_ASSIST_API_VERSION}:${method}`,
+				method: "POST",
+				headers: {
+					"Content-Type": "application/json",
+				},
+				responseType: "json",
+				data: JSON.stringify(body),
+			})
+			console.log(`[GeminiCLI] Response status:`, res.status)
+			console.log(`[GeminiCLI] Response data:`, JSON.stringify(res.data, null, 2))
+			return res.data
+		} catch (error: any) {
+			console.error(`[GeminiCLI] Error calling ${method}:`, error)
+			console.error(`[GeminiCLI] Error response:`, error.response?.data)
+			console.error(`[GeminiCLI] Error status:`, error.response?.status)
+			console.error(`[GeminiCLI] Error message:`, error.message)
+
+			// If we get a 401 and haven't retried yet, try refreshing auth
+			if (error.response?.status === 401 && retryAuth) {
+				console.log(`[GeminiCLI] Got 401, attempting to refresh authentication...`)
+				await this.ensureAuthenticated() // This will refresh the token
+				return this.callEndpoint(method, body, false) // Retry without further auth retries
+			}
+
+			throw error
+		}
+	}
+
+	/**
+	 * Discover or retrieve the project ID
+	 */
+	private async discoverProjectId(): Promise<string> {
+		// If we already have a project ID, use it
+		if (this.options.geminiCliProjectId) {
+			this.projectId = this.options.geminiCliProjectId
+			return this.projectId
+		}
+
+		// If we've already discovered it, return it
+		if (this.projectId) {
+			return this.projectId
+		}
+
+		// Start with a default project ID (can be anything for personal OAuth)
+		const initialProjectId = "default"
+
+		// Prepare client metadata
+		const clientMetadata = {
+			ideType: "IDE_UNSPECIFIED",
+			platform: "PLATFORM_UNSPECIFIED",
+			pluginType: "GEMINI",
+			duetProject: initialProjectId,
+		}
+
+		try {
+			// Call loadCodeAssist to discover the actual project ID
+			const loadRequest = {
+				cloudaicompanionProject: initialProjectId,
+				metadata: clientMetadata,
+			}
+
+			const loadResponse = await this.callEndpoint("loadCodeAssist", loadRequest)
+
+			// Check if we already have a project ID from the response
+			if (loadResponse.cloudaicompanionProject) {
+				this.projectId = loadResponse.cloudaicompanionProject
+				return this.projectId as string
+			}
+
+			// If no existing project, we need to onboard
+			const defaultTier = loadResponse.allowedTiers?.find((tier: any) => tier.isDefault)
+			const tierId = defaultTier?.id || "free-tier"
+
+			const onboardRequest = {
+				tierId: tierId,
+				cloudaicompanionProject: initialProjectId,
+				metadata: clientMetadata,
+			}
+
+			let lroResponse = await this.callEndpoint("onboardUser", onboardRequest)
+
+			// Poll until operation is complete
+			while (!lroResponse.done) {
+				await new Promise((resolve) => setTimeout(resolve, 2000))
+				lroResponse = await this.callEndpoint("onboardUser", onboardRequest)
+			}
+
+			const discoveredProjectId = lroResponse.response?.cloudaicompanionProject?.id || initialProjectId
+			this.projectId = discoveredProjectId
+			return this.projectId as string
+		} catch (error: any) {
+			console.error("Failed to discover project ID:", error.response?.data || error.message)
+			throw new Error("Could not discover project ID. Make sure you're authenticated with 'gemini auth'.")
+		}
+	}
+
+	/**
+	 * Parse Server-Sent Events from a stream
+	 */
+	private async *parseSSEStream(stream: NodeJS.ReadableStream): AsyncGenerator<any> {
+		let buffer = ""
+
+		for await (const chunk of stream) {
+			buffer += chunk.toString()
+			const lines = buffer.split("\n")
+			buffer = lines.pop() || ""
+
+			for (const line of lines) {
+				if (line.startsWith("data: ")) {
+					const data = line.slice(6).trim()
+					if (data === "[DONE]") continue
+
+					try {
+						const parsed = JSON.parse(data)
+						yield parsed
+					} catch (e) {
+						console.error("Error parsing SSE data:", e)
+					}
+				}
+			}
+		}
+	}
+
+	async *createMessage(
+		systemInstruction: string,
+		messages: Anthropic.Messages.MessageParam[],
+		metadata?: ApiHandlerCreateMessageMetadata,
+	): ApiStream {
+		await this.ensureAuthenticated()
+		const projectId = await this.discoverProjectId()
+
+		const { id: model, info, reasoning: thinkingConfig, maxTokens } = this.getModel()
+
+		// Convert messages to Gemini format
+		const contents = messages.map(convertAnthropicMessageToGemini)
+
+		// Prepare request body for Code Assist API - matching Cline's structure
+		const requestBody: any = {
+			model: model,
+			project: projectId,
+			request: {
+				contents: [
+					{
+						role: "user",
+						parts: [{ text: systemInstruction }],
+					},
+					...contents,
+				],
+				generationConfig: {
+					temperature: this.options.modelTemperature ?? 0.7,
+					maxOutputTokens: this.options.modelMaxTokens ?? maxTokens ?? 8192,
+				},
+			},
+		}
+
+		// Add thinking config if applicable
+		if (thinkingConfig) {
+			requestBody.request.generationConfig.thinkingConfig = thinkingConfig
+		}
+
+		console.log("[GeminiCLI] Request body:", JSON.stringify(requestBody, null, 2))
+
+		try {
+			// Call Code Assist streaming endpoint using OAuth2Client
+			const response = await this.authClient.request({
+				url: `${CODE_ASSIST_ENDPOINT}/${CODE_ASSIST_API_VERSION}:streamGenerateContent`,
+				method: "POST",
+				params: { alt: "sse" },
+				headers: {
+					"Content-Type": "application/json",
+				},
+				responseType: "stream",
+				data: JSON.stringify(requestBody),
+			})
+
+			// Process the SSE stream
+			let lastUsageMetadata: any = undefined
+
+			for await (const jsonData of this.parseSSEStream(response.data as NodeJS.ReadableStream)) {
+				// Extract content from the response
+				const responseData = jsonData.response || jsonData
+				const candidate = responseData.candidates?.[0]
+
+				if (candidate?.content?.parts) {
+					for (const part of candidate.content.parts) {
+						if (part.text) {
+							// Check if this is a thinking/reasoning part
+							if (part.thought === true) {
+								yield {
+									type: "reasoning",
+									text: part.text,
+								}
+							} else {
+								yield {
+									type: "text",
+									text: part.text,
+								}
+							}
+						}
+					}
+				}
+
+				// Store usage metadata for final reporting
+				if (responseData.usageMetadata) {
+					lastUsageMetadata = responseData.usageMetadata
+				}
+
+				// Check if this is the final chunk
+				if (candidate?.finishReason) {
+					break
+				}
+			}
+
+			// Yield final usage information
+			if (lastUsageMetadata) {
+				const inputTokens = lastUsageMetadata.promptTokenCount ?? 0
+				const outputTokens = lastUsageMetadata.candidatesTokenCount ?? 0
+				const cacheReadTokens = lastUsageMetadata.cachedContentTokenCount
+				const reasoningTokens = lastUsageMetadata.thoughtsTokenCount
+
+				yield {
+					type: "usage",
+					inputTokens,
+					outputTokens,
+					cacheReadTokens,
+					reasoningTokens,
+					totalCost: 0, // Free tier - all costs are 0
+				}
+			}
+		} catch (error: any) {
+			console.error("[GeminiCLI] API Error:", error.response?.status, error.response?.statusText)
+			console.error("[GeminiCLI] Error Response:", error.response?.data)
+
+			if (error.response?.status === 429) {
+				throw new Error("Rate limit exceeded. Free tier limits have been reached.")
+			}
+			if (error.response?.status === 400) {
+				throw new Error(`Bad request: ${JSON.stringify(error.response?.data) || error.message}`)
+			}
+			throw new Error(`Gemini CLI API error: ${error.message}`)
+		}
+	}
+
+	override getModel() {
+		const modelId = this.options.apiModelId
+		// Handle :thinking suffix before checking if model exists
+		const baseModelId = modelId?.endsWith(":thinking") ? modelId.replace(":thinking", "") : modelId
+		let id =
+			baseModelId && baseModelId in geminiCliModels ? (baseModelId as GeminiCliModelId) : geminiCliDefaultModelId
+		const info: ModelInfo = geminiCliModels[id]
+		const params = getModelParams({ format: "gemini", modelId: id, model: info, settings: this.options })
+
+		// Return the cleaned model ID
+		return { id, info, ...params }
+	}
+
+	async completePrompt(prompt: string): Promise<string> {
+		await this.ensureAuthenticated()
+		const projectId = await this.discoverProjectId()
+
+		try {
+			const { id: model } = this.getModel()
+
+			const requestBody = {
+				model: model,
+				project: projectId,
+				request: {
+					contents: [{ role: "user", parts: [{ text: prompt }] }],
+					generationConfig: {
+						temperature: this.options.modelTemperature ?? 0.7,
+					},
+				},
+			}
+
+			const response = await this.authClient.request({
+				url: `${CODE_ASSIST_ENDPOINT}/${CODE_ASSIST_API_VERSION}:generateContent`,
+				method: "POST",
+				headers: {
+					"Content-Type": "application/json",
+				},
+				data: JSON.stringify(requestBody),
+			})
+
+			// Extract text from response
+			const responseData = response.data as any
+			if (responseData.candidates && responseData.candidates.length > 0) {
+				const candidate = responseData.candidates[0]
+				if (candidate.content && candidate.content.parts) {
+					const textParts = candidate.content.parts
+						.filter((part: any) => part.text && !part.thought)
+						.map((part: any) => part.text)
+						.join("")
+					return textParts
+				}
+			}
+
+			return ""
+		} catch (error) {
+			if (error instanceof Error) {
+				throw new Error(`Gemini CLI completion error: ${error.message}`)
+			}
+			throw error
+		}
+	}
+
+	override async countTokens(content: Array<Anthropic.Messages.ContentBlockParam>): Promise<number> {
+		// For OAuth/free tier, we can't use the token counting API
+		// Fall back to the base provider's tiktoken implementation
+		return super.countTokens(content)
+	}
+}

+ 1 - 0
src/api/providers/index.ts

@@ -7,6 +7,7 @@ export { ClaudeCodeHandler } from "./claude-code"
 export { DeepSeekHandler } from "./deepseek"
 export { FakeAIHandler } from "./fake-ai"
 export { GeminiHandler } from "./gemini"
+export { GeminiCliHandler } from "./gemini-cli"
 export { GlamaHandler } from "./glama"
 export { GroqHandler } from "./groq"
 export { HumanRelayHandler } from "./human-relay"

+ 7 - 0
webview-ui/src/components/settings/ApiOptions.tsx

@@ -18,6 +18,7 @@ import {
 	anthropicDefaultModelId,
 	claudeCodeDefaultModelId,
 	geminiDefaultModelId,
+	geminiCliDefaultModelId,
 	deepSeekDefaultModelId,
 	mistralDefaultModelId,
 	xaiDefaultModelId,
@@ -51,6 +52,7 @@ import {
 	ClaudeCode,
 	DeepSeek,
 	Gemini,
+	GeminiCli,
 	Glama,
 	Groq,
 	LMStudio,
@@ -284,6 +286,7 @@ const ApiOptions = ({
 				"claude-code": { field: "apiModelId", default: claudeCodeDefaultModelId },
 				"openai-native": { field: "apiModelId", default: openAiNativeDefaultModelId },
 				gemini: { field: "apiModelId", default: geminiDefaultModelId },
+				"gemini-cli": { field: "apiModelId", default: geminiCliDefaultModelId },
 				deepseek: { field: "apiModelId", default: deepSeekDefaultModelId },
 				mistral: { field: "apiModelId", default: mistralDefaultModelId },
 				xai: { field: "apiModelId", default: xaiDefaultModelId },
@@ -531,6 +534,10 @@ const ApiOptions = ({
 				<Gemini apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
 			)}
 
+			{selectedProvider === "gemini-cli" && (
+				<GeminiCli apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
+			)}
+
 			{selectedProvider === "openai" && (
 				<OpenAICompatible
 					apiConfiguration={apiConfiguration}

+ 3 - 0
webview-ui/src/components/settings/constants.ts

@@ -6,6 +6,7 @@ import {
 	claudeCodeModels,
 	deepSeekModels,
 	geminiModels,
+	geminiCliModels,
 	mistralModels,
 	openAiNativeModels,
 	vertexModels,
@@ -22,6 +23,7 @@ export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, Mod
 	bedrock: bedrockModels,
 	deepseek: deepSeekModels,
 	gemini: geminiModels,
+	"gemini-cli": geminiCliModels,
 	fireworks: fireworksModels, // kilocode_change
 	mistral: mistralModels,
 	"openai-native": openAiNativeModels,
@@ -39,6 +41,7 @@ export const PROVIDERS = [
 	{ value: "fireworks", label: "Fireworks" }, // kilocode_change
 	{ value: "claude-code", label: "Claude Code" },
 	{ value: "gemini", label: "Google Gemini" },
+	{ value: "gemini-cli", label: "Gemini CLI Provider" },
 	{ value: "deepseek", label: "DeepSeek" },
 	{ value: "openai-native", label: "OpenAI" },
 	{ value: "openai", label: "OpenAI Compatible" },

+ 81 - 0
webview-ui/src/components/settings/providers/GeminiCli.tsx

@@ -0,0 +1,81 @@
+import { useCallback } from "react"
+import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
+
+import type { ProviderSettings } from "@roo-code/types"
+
+import { useAppTranslation } from "@src/i18n/TranslationContext"
+import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
+
+import { inputEventTransform } from "../transforms"
+
+type GeminiCliProps = {
+	apiConfiguration: ProviderSettings
+	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+}
+
+export const GeminiCli = ({ apiConfiguration, setApiConfigurationField }: GeminiCliProps) => {
+	const { t } = useAppTranslation()
+
+	const handleInputChange = useCallback(
+		<K extends keyof ProviderSettings, E>(
+			field: K,
+			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+		) =>
+			(event: E | Event) => {
+				setApiConfigurationField(field, transform(event as E))
+			},
+		[setApiConfigurationField],
+	)
+
+	return (
+		<>
+			<VSCodeTextField
+				value={apiConfiguration?.geminiCliOAuthPath || ""}
+				onInput={handleInputChange("geminiCliOAuthPath")}
+				placeholder="~/.gemini/oauth_creds.json"
+				className="w-full">
+				<label className="block font-medium mb-1">{t("settings:providers.geminiCli.oauthPath")}</label>
+			</VSCodeTextField>
+			<div className="text-sm text-vscode-descriptionForeground -mt-2">
+				{t("settings:providers.geminiCli.oauthPathDescription")}
+			</div>
+
+			<div className="text-sm text-vscode-descriptionForeground mt-3">
+				{t("settings:providers.geminiCli.description")}
+			</div>
+
+			<div className="text-sm text-vscode-descriptionForeground mt-2">
+				{t("settings:providers.geminiCli.instructions")}{" "}
+				<code className="text-vscode-textPreformat-foreground">gemini</code>{" "}
+				{t("settings:providers.geminiCli.instructionsContinued")}
+			</div>
+
+			<VSCodeLink
+				href="https://github.com/google-gemini/gemini-cli?tab=readme-ov-file#quickstart"
+				className="text-vscode-textLink-foreground hover:text-vscode-textLink-activeForeground mt-2 inline-block">
+				{t("settings:providers.geminiCli.setupLink")}
+			</VSCodeLink>
+
+			<div className="mt-3 p-3 bg-vscode-editorWidget-background border border-vscode-editorWidget-border rounded">
+				<div className="flex items-center gap-2 mb-2">
+					<i className="codicon codicon-warning text-vscode-notificationsWarningIcon-foreground" />
+					<span className="font-semibold text-sm">{t("settings:providers.geminiCli.requirementsTitle")}</span>
+				</div>
+				<ul className="list-disc list-inside space-y-1 text-sm text-vscode-descriptionForeground">
+					<li>{t("settings:providers.geminiCli.requirement1")}</li>
+					<li>{t("settings:providers.geminiCli.requirement2")}</li>
+					<li>{t("settings:providers.geminiCli.requirement3")}</li>
+					<li>{t("settings:providers.geminiCli.requirement4")}</li>
+					<li>{t("settings:providers.geminiCli.requirement5")}</li>
+				</ul>
+			</div>
+
+			<div className="mt-3 flex items-center gap-2">
+				<i className="codicon codicon-check text-vscode-notificationsInfoIcon-foreground" />
+				<span className="text-sm text-vscode-descriptionForeground">
+					{t("settings:providers.geminiCli.freeAccess")}
+				</span>
+			</div>
+		</>
+	)
+}

+ 169 - 0
webview-ui/src/components/settings/providers/__tests__/GeminiCli.tsx

@@ -0,0 +1,169 @@
+import { render, screen, fireEvent } from "@testing-library/react"
+import { describe, it, expect, vi } from "vitest"
+
+import type { ProviderSettings } from "@roo-code/types"
+
+import { GeminiCli } from "../GeminiCli"
+
+// Mock the translation hook
+vi.mock("@src/i18n/TranslationContext", () => ({
+	useAppTranslation: () => ({
+		t: (key: string) => key,
+	}),
+}))
+
+// Mock VSCodeLink to render as a regular anchor tag
+vi.mock("@vscode/webview-ui-toolkit/react", async () => {
+	const actual = await vi.importActual("@vscode/webview-ui-toolkit/react")
+	return {
+		...actual,
+		VSCodeLink: ({ children, href, ...props }: any) => (
+			<a href={href} {...props}>
+				{children}
+			</a>
+		),
+	}
+})
+
+describe("GeminiCli", () => {
+	const mockSetApiConfigurationField = vi.fn()
+	const defaultProps = {
+		apiConfiguration: {} as ProviderSettings,
+		setApiConfigurationField: mockSetApiConfigurationField,
+	}
+
+	beforeEach(() => {
+		vi.clearAllMocks()
+	})
+
+	it("renders all required elements", () => {
+		render(<GeminiCli {...defaultProps} />)
+
+		// Check for OAuth path input
+		expect(screen.getByText("settings:providers.geminiCli.oauthPath")).toBeInTheDocument()
+		expect(screen.getByPlaceholderText("~/.gemini/oauth_creds.json")).toBeInTheDocument()
+
+		// Check for description text
+		expect(screen.getByText("settings:providers.geminiCli.description")).toBeInTheDocument()
+
+		// Check for instructions - they're in the same div but broken up by the code element
+		// Find all elements that contain the instruction parts
+		const instructionsDivs = screen.getAllByText((_content, element) => {
+			// Check if this element contains all the expected text parts
+			const fullText = element?.textContent || ""
+			return (
+				fullText.includes("settings:providers.geminiCli.instructions") &&
+				fullText.includes("gemini") &&
+				fullText.includes("settings:providers.geminiCli.instructionsContinued")
+			)
+		})
+		// Find the div with the correct classes
+		const instructionsDiv = instructionsDivs.find(
+			(div) =>
+				div.classList.contains("text-sm") &&
+				div.classList.contains("text-vscode-descriptionForeground") &&
+				div.classList.contains("mt-2"),
+		)
+		expect(instructionsDiv).toBeDefined()
+		expect(instructionsDiv).toBeInTheDocument()
+
+		// Also verify the code element exists
+		const codeElement = screen.getByText("gemini")
+		expect(codeElement).toBeInTheDocument()
+		expect(codeElement.tagName).toBe("CODE")
+
+		// Check for setup link
+		expect(screen.getByText("settings:providers.geminiCli.setupLink")).toBeInTheDocument()
+
+		// Check for requirements
+		expect(screen.getByText("settings:providers.geminiCli.requirementsTitle")).toBeInTheDocument()
+		expect(screen.getByText("settings:providers.geminiCli.requirement1")).toBeInTheDocument()
+		expect(screen.getByText("settings:providers.geminiCli.requirement2")).toBeInTheDocument()
+		expect(screen.getByText("settings:providers.geminiCli.requirement3")).toBeInTheDocument()
+		expect(screen.getByText("settings:providers.geminiCli.requirement4")).toBeInTheDocument()
+		expect(screen.getByText("settings:providers.geminiCli.requirement5")).toBeInTheDocument()
+
+		// Check for free access note
+		expect(screen.getByText("settings:providers.geminiCli.freeAccess")).toBeInTheDocument()
+	})
+
+	it("displays OAuth path from configuration", () => {
+		const apiConfiguration: ProviderSettings = {
+			geminiCliOAuthPath: "/custom/path/oauth.json",
+		}
+
+		render(<GeminiCli {...defaultProps} apiConfiguration={apiConfiguration} />)
+
+		const oauthInput = screen.getByDisplayValue("/custom/path/oauth.json")
+		expect(oauthInput).toBeInTheDocument()
+	})
+
+	it("calls setApiConfigurationField when OAuth path is changed", () => {
+		render(<GeminiCli {...defaultProps} />)
+
+		const oauthInput = screen.getByPlaceholderText("~/.gemini/oauth_creds.json")
+
+		// Simulate input event with VSCodeTextField
+		fireEvent.input(oauthInput, { target: { value: "/new/path.json" } })
+
+		// Check that setApiConfigurationField was called
+		expect(mockSetApiConfigurationField).toHaveBeenCalledWith("geminiCliOAuthPath", "/new/path.json")
+	})
+
+	it("renders setup link with correct href", () => {
+		render(<GeminiCli {...defaultProps} />)
+
+		const setupLink = screen.getByText("settings:providers.geminiCli.setupLink")
+		expect(setupLink).toHaveAttribute(
+			"href",
+			"https://github.com/google-gemini/gemini-cli?tab=readme-ov-file#quickstart",
+		)
+	})
+
+	it("shows OAuth path description", () => {
+		render(<GeminiCli {...defaultProps} />)
+
+		expect(screen.getByText("settings:providers.geminiCli.oauthPathDescription")).toBeInTheDocument()
+	})
+
+	it("renders all requirements in a list", () => {
+		render(<GeminiCli {...defaultProps} />)
+
+		const listItems = screen.getAllByRole("listitem")
+		expect(listItems).toHaveLength(5)
+		expect(listItems[0]).toHaveTextContent("settings:providers.geminiCli.requirement1")
+		expect(listItems[1]).toHaveTextContent("settings:providers.geminiCli.requirement2")
+		expect(listItems[2]).toHaveTextContent("settings:providers.geminiCli.requirement3")
+		expect(listItems[3]).toHaveTextContent("settings:providers.geminiCli.requirement4")
+		expect(listItems[4]).toHaveTextContent("settings:providers.geminiCli.requirement5")
+	})
+
+	it("applies correct styling classes", () => {
+		render(<GeminiCli {...defaultProps} />)
+
+		// Check for styled warning box
+		const warningBox = screen.getByText("settings:providers.geminiCli.requirementsTitle").closest("div.mt-3")
+		expect(warningBox).toHaveClass("bg-vscode-editorWidget-background")
+		expect(warningBox).toHaveClass("border-vscode-editorWidget-border")
+		expect(warningBox).toHaveClass("rounded")
+		expect(warningBox).toHaveClass("p-3")
+
+		// Check for warning icon
+		const warningIcon = screen.getByText("settings:providers.geminiCli.requirementsTitle").previousElementSibling
+		expect(warningIcon).toHaveClass("codicon-warning")
+		expect(warningIcon).toHaveClass("text-vscode-notificationsWarningIcon-foreground")
+
+		// Check for check icon
+		const checkIcon = screen.getByText("settings:providers.geminiCli.freeAccess").previousElementSibling
+		expect(checkIcon).toHaveClass("codicon-check")
+		expect(checkIcon).toHaveClass("text-vscode-notificationsInfoIcon-foreground")
+	})
+
+	it("renders instructions with code element", () => {
+		render(<GeminiCli {...defaultProps} />)
+
+		const codeElement = screen.getByText("gemini")
+		expect(codeElement.tagName).toBe("CODE")
+		expect(codeElement).toHaveClass("text-vscode-textPreformat-foreground")
+	})
+})

+ 1 - 0
webview-ui/src/components/settings/providers/index.ts

@@ -4,6 +4,7 @@ export { Chutes } from "./Chutes"
 export { ClaudeCode } from "./ClaudeCode"
 export { DeepSeek } from "./DeepSeek"
 export { Gemini } from "./Gemini"
+export { GeminiCli } from "./GeminiCli"
 export { Glama } from "./Glama"
 export { Groq } from "./Groq"
 export { LMStudio } from "./LMStudio"

+ 7 - 0
webview-ui/src/components/ui/hooks/useSelectedModel.ts

@@ -10,6 +10,8 @@ import {
 	deepSeekModels,
 	geminiDefaultModelId,
 	geminiModels,
+	geminiCliDefaultModelId,
+	geminiCliModels,
 	mistralDefaultModelId,
 	mistralModels,
 	openAiModelInfoSaneDefaults,
@@ -172,6 +174,11 @@ function getSelectedModel({
 			const info = geminiModels[id as keyof typeof geminiModels]
 			return { id, info }
 		}
+		case "gemini-cli": {
+			const id = apiConfiguration.apiModelId ?? geminiCliDefaultModelId
+			const info = geminiCliModels[id as keyof typeof geminiCliModels]
+			return { id, info }
+		}
 		case "deepseek": {
 			const id = apiConfiguration.apiModelId ?? deepSeekDefaultModelId
 			const info = deepSeekModels[id as keyof typeof deepSeekModels]

+ 15 - 0
webview-ui/src/i18n/locales/ca/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Ruta del Codi Claude",
 			"description": "Ruta opcional al teu CLI de Claude Code. Per defecte, 'claude' si no s'estableix.",
 			"placeholder": "Per defecte: claude"
+		},
+		"geminiCli": {
+			"description": "Aquest proveïdor utilitza l'autenticació OAuth de l'eina Gemini CLI i no requereix claus API.",
+			"oauthPath": "Ruta de credencials OAuth (opcional)",
+			"oauthPathDescription": "Ruta al fitxer de credencials OAuth. Deixa-ho buit per utilitzar la ubicació per defecte (~/.gemini/oauth_creds.json).",
+			"instructions": "Si encara no t'has autenticat, si us plau executa",
+			"instructionsContinued": "al teu terminal primer.",
+			"setupLink": "Instruccions de configuració de Gemini CLI",
+			"requirementsTitle": "Requisits importants",
+			"requirement1": "Primer, necessites instal·lar l'eina Gemini CLI",
+			"requirement2": "Després, executa gemini al teu terminal i assegura't d'iniciar sessió amb Google",
+			"requirement3": "Només funciona amb comptes personals de Google (no comptes de Google Workspace)",
+			"requirement4": "No utilitza claus API - l'autenticació es gestiona via OAuth",
+			"requirement5": "Requereix que l'eina Gemini CLI estigui instal·lada i autenticada primer",
+			"freeAccess": "Accés de nivell gratuït via autenticació OAuth"
 		}
 	},
 	"browser": {

+ 30 - 0
webview-ui/src/i18n/locales/cs/settings.json

@@ -317,6 +317,36 @@
 			"pathLabel": "Cesta ke Claude Code",
 			"description": "Volitelná cesta k vašemu CLI Claude Code. Ve výchozím nastavení 'claude', pokud není nastaveno.",
 			"placeholder": "Výchozí: claude"
+		},
+		"geminiCli": {
+			"description": "Tento poskytovatel používá OAuth autentizaci z nástroje Gemini CLI a nevyžaduje klíče API.",
+			"oauthPath": "Cesta k OAuth přihlašovacím údajům (volitelné)",
+			"oauthPathDescription": "Cesta k souboru přihlašovacích údajů OAuth. Ponechte prázdné pro použití výchozího umístění (~/.gemini/oauth_creds.json).",
+			"instructions": "Pokud jste se ještě neověřili, spusťte prosím",
+			"instructionsContinued": "ve vašem terminálu nejdříve.",
+			"setupLink": "Pokyny k nastavení Gemini CLI",
+			"requirementsTitle": "Důležité požadavky",
+			"requirement1": "Nejprve musíte nainstalovat nástroj Gemini CLI",
+			"requirement2": "Poté spusťte gemini ve vašem terminálu a ujistěte se, že se přihlásíte pomocí Google",
+			"requirement3": "Funguje pouze s osobními účty Google (ne s účty Google Workspace)",
+			"requirement4": "Nepoužívá klíče API - ověřování je řešeno přes OAuth",
+			"requirement5": "Vyžaduje, aby byl nástroj Gemini CLI nejdříve nainstalován a ověřen",
+			"freeAccess": "Přístup k bezplatné úrovni prostřednictvím OAuth autentizace"
+		},
+		"geminiCli": {
+			"description": "Tento poskytovatel používá OAuth autentifikaci nástroje Gemini CLI a nevyžaduje API klíče.",
+			"oauthPath": "Cesta k OAuth přihlašovacím údajům (volitelné)",
+			"oauthPathDescription": "Cesta k souboru OAuth přihlašovacích údajů. Nechte prázdné pro použití výchozího umístění (~/.gemini/oauth_creds.json).",
+			"instructions": "Pokud jste se ještě neověřili, spusťte",
+			"instructionsContinued": "ve svém terminálu nejprve.",
+			"setupLink": "Pokyny k nastavení Gemini CLI",
+			"requirementsTitle": "Důležité požadavky",
+			"requirement1": "Nejprve musíte nainstalovat nástroj Gemini CLI",
+			"requirement2": "Poté spusťte gemini ve svém terminálu a ujistěte se, že se přihlásíte pomocí Google",
+			"requirement3": "Funguje pouze s osobními Google účty (ne Google Workspace účty)",
+			"requirement4": "Nepoužívá API klíče - autentifikace je zpracována přes OAuth",
+			"requirement5": "Vyžaduje, aby byl nástroj Gemini CLI nejprve nainstalován a ověřen",
+			"freeAccess": "Bezplatný přístup přes OAuth autentifikaci"
 		}
 	},
 	"browser": {

+ 30 - 0
webview-ui/src/i18n/locales/de/settings.json

@@ -317,6 +317,36 @@
 			"pathLabel": "Claude-Code-Pfad",
 			"description": "Optionaler Pfad zu Ihrer Claude Code CLI. Standard ist 'claude', wenn nicht festgelegt.",
 			"placeholder": "Standard: claude"
+		},
+		"geminiCli": {
+			"description": "Dieser Anbieter nutzt OAuth-Authentifizierung über das Gemini CLI-Tool und benötigt keine API-Schlüssel.",
+			"oauthPath": "OAuth-Anmeldedatenpfad (optional)",
+			"oauthPathDescription": "Pfad zur OAuth-Anmeldedatei. Leer lassen, um den Standardspeicherort (~/.gemini/oauth_creds.json) zu verwenden.",
+			"instructions": "Wenn du dich noch nicht authentifiziert hast, führe bitte",
+			"instructionsContinued": "zuerst in deinem Terminal aus.",
+			"setupLink": "Gemini CLI-Einrichtungsanleitung",
+			"requirementsTitle": "Wichtige Anforderungen",
+			"requirement1": "Zuerst musst du das Gemini CLI-Tool installieren",
+			"requirement2": "Dann führe gemini in deinem Terminal aus und stelle sicher, dass du dich mit Google anmeldest",
+			"requirement3": "Funktioniert nur mit persönlichen Google-Konten (nicht mit Google Workspace-Konten)",
+			"requirement4": "Verwendet keine API-Schlüssel - die Authentifizierung erfolgt über OAuth",
+			"requirement5": "Erfordert, dass das Gemini CLI-Tool zuerst installiert und authentifiziert wird",
+			"freeAccess": "Kostenloser Zugang über OAuth-Authentifizierung"
+		},
+		"geminiCli": {
+			"description": "Dieser Provider verwendet OAuth-Authentifizierung des Gemini CLI-Tools und benötigt keine API-Schlüssel.",
+			"oauthPath": "OAuth-Anmeldedaten-Pfad (optional)",
+			"oauthPathDescription": "Pfad zur OAuth-Anmeldedaten-Datei. Leer lassen, um den Standardort zu verwenden (~/.gemini/oauth_creds.json).",
+			"instructions": "Falls du dich noch nicht authentifiziert hast, führe bitte",
+			"instructionsContinued": "in deinem Terminal zuerst aus.",
+			"setupLink": "Gemini CLI Setup-Anweisungen",
+			"requirementsTitle": "Wichtige Anforderungen",
+			"requirement1": "Zuerst musst du das Gemini CLI-Tool installieren",
+			"requirement2": "Dann führe gemini in deinem Terminal aus und stelle sicher, dass du dich mit Google anmeldest",
+			"requirement3": "Funktioniert nur mit persönlichen Google-Konten (nicht mit Google Workspace-Konten)",
+			"requirement4": "Verwendet keine API-Schlüssel - Authentifizierung wird über OAuth abgewickelt",
+			"requirement5": "Erfordert, dass das Gemini CLI-Tool zuerst installiert und authentifiziert wird",
+			"freeAccess": "Kostenloses Tier-Zugang über OAuth-Authentifizierung"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/el/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Διαδρομή Claude Code",
 			"description": "Προαιρετική διαδρομή προς το Claude Code CLI. Προεπιλογή 'claude' εάν δεν οριστεί.",
 			"placeholder": "Προεπιλογή: claude"
+		},
+		"geminiCli": {
+			"description": "Αυτός ο πάροχος χρησιμοποιεί την αυθεντικοποίηση OAuth του εργαλείου Gemini CLI και δεν απαιτεί κλειδιά API.",
+			"oauthPath": "Διαδρομή διαπιστευτηρίων OAuth (προαιρετικό)",
+			"oauthPathDescription": "Διαδρομή προς το αρχείο διαπιστευτηρίων OAuth. Αφήστε κενό για χρήση της προεπιλεγμένης τοποθεσίας (~/.gemini/oauth_creds.json).",
+			"instructions": "Αν δεν έχεις αυθεντικοποιηθεί ακόμα, εκτέλεσε",
+			"instructionsContinued": "στο τερματικό σου πρώτα.",
+			"setupLink": "Οδηγίες εγκατάστασης Gemini CLI",
+			"requirementsTitle": "Σημαντικές απαιτήσεις",
+			"requirement1": "Πρώτα, πρέπει να εγκαταστήσεις το εργαλείο Gemini CLI",
+			"requirement2": "Έπειτα, εκτέλεσε gemini στο τερματικό σου και βεβαιώσου ότι συνδέεσαι με Google",
+			"requirement3": "Λειτουργεί μόνο με προσωπικούς λογαριασμούς Google (όχι λογαριασμούς Google Workspace)",
+			"requirement4": "Δεν χρησιμοποιεί κλειδιά API - η αυθεντικοποίηση διαχειρίζεται μέσω OAuth",
+			"requirement5": "Απαιτεί το εργαλείο Gemini CLI να είναι εγκατεστημένο και αυθεντικοποιημένο πρώτα",
+			"freeAccess": "Δωρεάν πρόσβαση μέσω αυθεντικοποίησης OAuth"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/en/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Claude Code Path",
 			"description": "Optional path to your Claude Code CLI. Defaults to 'claude' if not set.",
 			"placeholder": "Default: claude"
+		},
+		"geminiCli": {
+			"description": "This provider uses OAuth authentication from the Gemini CLI tool and does not require API keys.",
+			"oauthPath": "OAuth Credentials Path (optional)",
+			"oauthPathDescription": "Path to the OAuth credentials file. Leave empty to use the default location (~/.gemini/oauth_creds.json).",
+			"instructions": "If you haven't authenticated yet, please run",
+			"instructionsContinued": "in your terminal first.",
+			"setupLink": "Gemini CLI Setup Instructions",
+			"requirementsTitle": "Important Requirements",
+			"requirement1": "First, you need to install the Gemini CLI tool",
+			"requirement2": "Then, run gemini in your terminal and make sure you Log in with Google",
+			"requirement3": "Only works with personal Google accounts (not Google Workspace accounts)",
+			"requirement4": "Does not use API keys - authentication is handled via OAuth",
+			"requirement5": "Requires the Gemini CLI tool to be installed and authenticated first",
+			"freeAccess": "Free tier access via OAuth authentication"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/es/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Ruta de Claude Code",
 			"description": "Ruta opcional a su CLI de Claude Code. Por defecto, es 'claude' si no se establece.",
 			"placeholder": "Por defecto: claude"
+		},
+		"geminiCli": {
+			"description": "Este proveedor utiliza autenticación OAuth de la herramienta Gemini CLI y no requiere claves API.",
+			"oauthPath": "Ruta de credenciales OAuth (opcional)",
+			"oauthPathDescription": "Ruta al archivo de credenciales OAuth. Déjalo vacío para usar la ubicación predeterminada (~/.gemini/oauth_creds.json).",
+			"instructions": "Si aún no te has autenticado, por favor ejecuta",
+			"instructionsContinued": "en tu terminal primero.",
+			"setupLink": "Instrucciones de configuración de Gemini CLI",
+			"requirementsTitle": "Requisitos importantes",
+			"requirement1": "Primero, necesitas instalar la herramienta Gemini CLI",
+			"requirement2": "Luego, ejecuta gemini en tu terminal y asegúrate de iniciar sesión con Google",
+			"requirement3": "Solo funciona con cuentas personales de Google (no cuentas de Google Workspace)",
+			"requirement4": "No usa claves API - la autenticación se maneja vía OAuth",
+			"requirement5": "Requiere que la herramienta Gemini CLI esté instalada y autenticada primero",
+			"freeAccess": "Acceso de nivel gratuito vía autenticación OAuth"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/fil/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Claude Code Path",
 			"description": "Opsyonal na path sa iyong Claude Code CLI. Default sa 'claude' kung hindi nakatakda.",
 			"placeholder": "Default: claude"
+		},
+		"geminiCli": {
+			"description": "Ang provider na ito ay gumagamit ng OAuth authentication ng Gemini CLI tool at hindi nangangailangan ng mga API key.",
+			"oauthPath": "OAuth Credentials Path (opsyonal)",
+			"oauthPathDescription": "Path sa OAuth credentials file. Iwanang blank para gamitin ang default na lokasyon (~/.gemini/oauth_creds.json).",
+			"instructions": "Kung hindi ka pa nag-authenticate, mangyaring patakbuhin ang",
+			"instructionsContinued": "sa iyong terminal muna.",
+			"setupLink": "Gemini CLI Setup Instructions",
+			"requirementsTitle": "Mahalagang Requirements",
+			"requirement1": "Una, kailangan mong i-install ang Gemini CLI tool",
+			"requirement2": "Pagkatapos, patakbuhin ang gemini sa iyong terminal at siguraduhing mag-log in gamit ang Google",
+			"requirement3": "Gumagana lang sa personal na Google accounts (hindi Google Workspace accounts)",
+			"requirement4": "Hindi gumagamit ng API keys - ang authentication ay ginagawa sa pamamagitan ng OAuth",
+			"requirement5": "Nangangailangan na ma-install at ma-authenticate muna ang Gemini CLI tool",
+			"freeAccess": "Free tier access sa pamamagitan ng OAuth authentication"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/fr/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Chemin du code Claude",
 			"description": "Chemin facultatif vers votre CLI Claude Code. La valeur par défaut est 'claude' si non défini.",
 			"placeholder": "Défaut : claude"
+		},
+		"geminiCli": {
+			"description": "Ce fournisseur utilise l'authentification OAuth de l'outil Gemini CLI et ne nécessite pas de clés API.",
+			"oauthPath": "Chemin des identifiants OAuth (optionnel)",
+			"oauthPathDescription": "Chemin vers le fichier d'identifiants OAuth. Laissez vide pour utiliser l'emplacement par défaut (~/.gemini/oauth_creds.json).",
+			"instructions": "Si tu ne t'es pas encore authentifié, veuillez exécuter",
+			"instructionsContinued": "dans ton terminal d'abord.",
+			"setupLink": "Instructions de configuration de Gemini CLI",
+			"requirementsTitle": "Exigences importantes",
+			"requirement1": "D'abord, tu dois installer l'outil Gemini CLI",
+			"requirement2": "Ensuite, exécute gemini dans ton terminal et assure-toi de te connecter avec Google",
+			"requirement3": "Fonctionne uniquement avec les comptes Google personnels (pas les comptes Google Workspace)",
+			"requirement4": "N'utilise pas de clés API - l'authentification est gérée via OAuth",
+			"requirement5": "Nécessite que l'outil Gemini CLI soit installé et authentifié d'abord",
+			"freeAccess": "Accès de niveau gratuit via l'authentification OAuth"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/hi/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "क्लाउड कोड पथ",
 			"description": "आपके क्लाउड कोड सीएलआई का वैकल्पिक पथ। यदि सेट नहीं है तो डिफ़ॉल्ट 'claude' है।",
 			"placeholder": "डिफ़ॉल्ट: claude"
+		},
+		"geminiCli": {
+			"description": "यह प्रदाता Gemini CLI टूल के OAuth प्रमाणीकरण का उपयोग करता है और API कुंजियों की आवश्यकता नहीं है।",
+			"oauthPath": "OAuth क्रेडेंशियल पथ (वैकल्पिक)",
+			"oauthPathDescription": "OAuth क्रेडेंशियल फ़ाइल का पथ। डिफ़ॉल्ट स्थान (~/.gemini/oauth_creds.json) का उपयोग करने के लिए खाली छोड़ें।",
+			"instructions": "यदि तुमने अभी तक प्रमाणीकरण नहीं किया है, तो कृपया चलाएं",
+			"instructionsContinued": "अपने टर्मिनल में पहले।",
+			"setupLink": "Gemini CLI सेटअप निर्देश",
+			"requirementsTitle": "महत्वपूर्ण आवश्यकताएं",
+			"requirement1": "पहले, तुम्हें Gemini CLI टूल इंस्टॉल करना होगा",
+			"requirement2": "फिर, अपने टर्मिनल में gemini चलाएं और सुनिश्चित करें कि तुम Google के साथ लॉग इन करो",
+			"requirement3": "केवल व्यक्तिगत Google खातों के साथ काम करता है (Google Workspace खाते नहीं)",
+			"requirement4": "API कुंजियों का उपयोग नहीं करता - प्रमाणीकरण OAuth के माध्यम से संभाला जाता है",
+			"requirement5": "आवश्यक है कि Gemini CLI टूल पहले इंस्टॉल और प्रमाणित हो",
+			"freeAccess": "OAuth प्रमाणीकरण के माध्यम से मुफ्त टियर पहुंच"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/id/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Jalur Kode Claude",
 			"description": "Jalur opsional ke Claude Code CLI Anda. Defaultnya adalah 'claude' jika tidak diatur.",
 			"placeholder": "Default: claude"
+		},
+		"geminiCli": {
+			"description": "Provider ini menggunakan autentikasi OAuth dari alat Gemini CLI dan tidak memerlukan kunci API.",
+			"oauthPath": "Jalur Kredensial OAuth (opsional)",
+			"oauthPathDescription": "Jalur ke file kredensial OAuth. Biarkan kosong untuk menggunakan lokasi default (~/.gemini/oauth_creds.json).",
+			"instructions": "Jika kamu belum melakukan autentikasi, silakan jalankan",
+			"instructionsContinued": "di terminal kamu terlebih dahulu.",
+			"setupLink": "Instruksi Pengaturan Gemini CLI",
+			"requirementsTitle": "Persyaratan Penting",
+			"requirement1": "Pertama, kamu perlu menginstal alat Gemini CLI",
+			"requirement2": "Kemudian, jalankan gemini di terminal kamu dan pastikan kamu masuk dengan Google",
+			"requirement3": "Hanya bekerja dengan akun Google pribadi (bukan akun Google Workspace)",
+			"requirement4": "Tidak menggunakan kunci API - autentikasi ditangani melalui OAuth",
+			"requirement5": "Memerlukan alat Gemini CLI diinstal dan diautentikasi terlebih dahulu",
+			"freeAccess": "Akses tingkat gratis melalui autentikasi OAuth"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/it/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Percorso Claude Code",
 			"description": "Percorso facoltativo per la tua CLI Claude Code. Predefinito 'claude' se non impostato.",
 			"placeholder": "Predefinito: claude"
+		},
+		"geminiCli": {
+			"description": "Questo provider utilizza l'autenticazione OAuth dello strumento Gemini CLI e non richiede chiavi API.",
+			"oauthPath": "Percorso credenziali OAuth (opzionale)",
+			"oauthPathDescription": "Percorso al file delle credenziali OAuth. Lascia vuoto per utilizzare la posizione predefinita (~/.gemini/oauth_creds.json).",
+			"instructions": "Se non ti sei ancora autenticato, esegui",
+			"instructionsContinued": "nel tuo terminale prima.",
+			"setupLink": "Istruzioni di configurazione Gemini CLI",
+			"requirementsTitle": "Requisiti importanti",
+			"requirement1": "Prima, devi installare lo strumento Gemini CLI",
+			"requirement2": "Poi, esegui gemini nel tuo terminale e assicurati di accedere con Google",
+			"requirement3": "Funziona solo con account Google personali (non account Google Workspace)",
+			"requirement4": "Non usa chiavi API - l'autenticazione è gestita tramite OAuth",
+			"requirement5": "Richiede che lo strumento Gemini CLI sia installato e autenticato prima",
+			"freeAccess": "Accesso di livello gratuito tramite autenticazione OAuth"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/ja/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "クロードコードパス",
 			"description": "Claude Code CLIへのオプションパス。設定されていない場合、デフォルトは「claude」です。",
 			"placeholder": "デフォルト:claude"
+		},
+		"geminiCli": {
+			"description": "このプロバイダーは Gemini CLI ツールの OAuth 認証を使用し、API キーは必要ありません。",
+			"oauthPath": "OAuth 認証情報パス(オプション)",
+			"oauthPathDescription": "OAuth 認証情報ファイルへのパス。空のままにするとデフォルトの場所(~/.gemini/oauth_creds.json)を使用します。",
+			"instructions": "まだ認証していない場合は、",
+			"instructionsContinued": "をターミナルで最初に実行してください。",
+			"setupLink": "Gemini CLI セットアップ手順",
+			"requirementsTitle": "重要な要件",
+			"requirement1": "まず、Gemini CLI ツールをインストールする必要があります",
+			"requirement2": "次に、ターミナルで gemini を実行し、Google でログインしてください",
+			"requirement3": "個人の Google アカウントでのみ動作します(Google Workspace アカウントは不可)",
+			"requirement4": "API キーは使用しません - 認証は OAuth 経由で処理されます",
+			"requirement5": "Gemini CLI ツールが最初にインストールされ、認証されている必要があります",
+			"freeAccess": "OAuth 認証による無料ティアアクセス"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/ko/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "클로드 코드 경로",
 			"description": "Claude Code CLI의 선택적 경로입니다. 설정하지 않으면 'claude'가 기본값입니다.",
 			"placeholder": "기본값: claude"
+		},
+		"geminiCli": {
+			"description": "이 공급자는 Gemini CLI 도구의 OAuth 인증을 사용하며 API 키가 필요하지 않습니다.",
+			"oauthPath": "OAuth 자격 증명 경로 (선택사항)",
+			"oauthPathDescription": "OAuth 자격 증명 파일 경로입니다. 기본 위치(~/.gemini/oauth_creds.json)를 사용하려면 비워두세요.",
+			"instructions": "아직 인증하지 않았다면",
+			"instructionsContinued": "을 터미널에서 먼저 실행해주세요.",
+			"setupLink": "Gemini CLI 설정 지침",
+			"requirementsTitle": "중요한 요구사항",
+			"requirement1": "먼저 Gemini CLI 도구를 설치해야 합니다",
+			"requirement2": "그다음 터미널에서 gemini를 실행하고 Google로 로그인하세요",
+			"requirement3": "개인 Google 계정에서만 작동합니다 (Google Workspace 계정 불가)",
+			"requirement4": "API 키를 사용하지 않습니다 - 인증은 OAuth를 통해 처리됩니다",
+			"requirement5": "Gemini CLI 도구가 먼저 설치되고 인증되어야 합니다",
+			"freeAccess": "OAuth 인증을 통한 무료 등급 액세스"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/nl/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Claude Code Pad",
 			"description": "Optioneel pad naar uw Claude Code CLI. Standaard 'claude' als niet ingesteld.",
 			"placeholder": "Standaard: claude"
+		},
+		"geminiCli": {
+			"description": "Deze provider gebruikt OAuth-authenticatie van het Gemini CLI-tool en vereist geen API-sleutels.",
+			"oauthPath": "OAuth-referentiepad (optioneel)",
+			"oauthPathDescription": "Pad naar het OAuth-referentiebestand. Laat leeg om de standaardlocatie te gebruiken (~/.gemini/oauth_creds.json).",
+			"instructions": "Als je nog niet bent geauthenticeerd, voer dan",
+			"instructionsContinued": "eerst uit in je terminal.",
+			"setupLink": "Gemini CLI-installatie-instructies",
+			"requirementsTitle": "Belangrijke vereisten",
+			"requirement1": "Eerst moet je het Gemini CLI-tool installeren",
+			"requirement2": "Voer daarna gemini uit in je terminal en zorg ervoor dat je inlogt met Google",
+			"requirement3": "Werkt alleen met persoonlijke Google-accounts (geen Google Workspace-accounts)",
+			"requirement4": "Gebruikt geen API-sleutels - authenticatie wordt afgehandeld via OAuth",
+			"requirement5": "Vereist dat het Gemini CLI-tool eerst wordt geïnstalleerd en geauthenticeerd",
+			"freeAccess": "Gratis tier-toegang via OAuth-authenticatie"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/pl/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Ścieżka Claude Code",
 			"description": "Opcjonalna ścieżka do Twojego CLI Claude Code. Domyślnie 'claude', jeśli nie ustawiono.",
 			"placeholder": "Domyślnie: claude"
+		},
+		"geminiCli": {
+			"description": "Ten dostawca używa uwierzytelniania OAuth z narzędzia Gemini CLI i nie wymaga kluczy API.",
+			"oauthPath": "Ścieżka danych uwierzytelniających OAuth (opcjonalna)",
+			"oauthPathDescription": "Ścieżka do pliku danych uwierzytelniających OAuth. Pozostaw puste, aby użyć domyślnej lokalizacji (~/.gemini/oauth_creds.json).",
+			"instructions": "Jeśli jeszcze się nie uwierzytelniłeś, uruchom",
+			"instructionsContinued": "w swoim terminalu najpierw.",
+			"setupLink": "Instrukcje konfiguracji Gemini CLI",
+			"requirementsTitle": "Ważne wymagania",
+			"requirement1": "Najpierw musisz zainstalować narzędzie Gemini CLI",
+			"requirement2": "Następnie uruchom gemini w swoim terminalu i upewnij się, że logujesz się przez Google",
+			"requirement3": "Działa tylko z osobistymi kontami Google (nie kontami Google Workspace)",
+			"requirement4": "Nie używa kluczy API - uwierzytelnianie jest obsługiwane przez OAuth",
+			"requirement5": "Wymaga, aby narzędzie Gemini CLI było najpierw zainstalowane i uwierzytelnione",
+			"freeAccess": "Dostęp do warstwy bezpłatnej przez uwierzytelnianie OAuth"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/pt-BR/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Caminho do Claude Code",
 			"description": "Caminho opcional para o seu Claude Code CLI. O padrão é 'claude' se não for definido.",
 			"placeholder": "Padrão: claude"
+		},
+		"geminiCli": {
+			"description": "Este provedor usa autenticação OAuth da ferramenta Gemini CLI e não requer chaves de API.",
+			"oauthPath": "Caminho das credenciais OAuth (opcional)",
+			"oauthPathDescription": "Caminho para o arquivo de credenciais OAuth. Deixe vazio para usar a localização padrão (~/.gemini/oauth_creds.json).",
+			"instructions": "Se você ainda não se autenticou, execute",
+			"instructionsContinued": "no seu terminal primeiro.",
+			"setupLink": "Instruções de configuração do Gemini CLI",
+			"requirementsTitle": "Requisitos importantes",
+			"requirement1": "Primeiro, você precisa instalar a ferramenta Gemini CLI",
+			"requirement2": "Depois, execute gemini no seu terminal e certifique-se de fazer login com o Google",
+			"requirement3": "Funciona apenas com contas pessoais do Google (não contas do Google Workspace)",
+			"requirement4": "Não usa chaves de API - autenticação é feita via OAuth",
+			"requirement5": "Requer que a ferramenta Gemini CLI seja instalada e autenticada primeiro",
+			"freeAccess": "Acesso de nível gratuito via autenticação OAuth"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/ru/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Путь к Claude Code",
 			"description": "Необязательный путь к вашему Claude Code CLI. По умолчанию используется 'claude', если не установлено.",
 			"placeholder": "По умолчанию: claude"
+		},
+		"geminiCli": {
+			"description": "Этот провайдер использует OAuth-аутентификацию инструмента Gemini CLI и не требует API-ключей.",
+			"oauthPath": "Путь к учетным данным OAuth (необязательно)",
+			"oauthPathDescription": "Путь к файлу учетных данных OAuth. Оставьте пустым для использования местоположения по умолчанию (~/.gemini/oauth_creds.json).",
+			"instructions": "Если ты еще не прошел аутентификацию, выполни",
+			"instructionsContinued": "в своем терминале сначала.",
+			"setupLink": "Инструкции по настройке Gemini CLI",
+			"requirementsTitle": "Важные требования",
+			"requirement1": "Сначала тебе нужно установить инструмент Gemini CLI",
+			"requirement2": "Затем запусти gemini в своем терминале и убедись, что войдешь в систему через Google",
+			"requirement3": "Работает только с личными аккаунтами Google (не аккаунты Google Workspace)",
+			"requirement4": "Не использует API-ключи - аутентификация выполняется через OAuth",
+			"requirement5": "Требует, чтобы инструмент Gemini CLI был сначала установлен и аутентифицирован",
+			"freeAccess": "Доступ к бесплатному уровню через OAuth-аутентификацию"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/sv/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Sökväg till Claude Code",
 			"description": "Valfri sökväg till din Claude Code CLI. Standard är 'claude' om den inte är inställd.",
 			"placeholder": "Standard: claude"
+		},
+		"geminiCli": {
+			"description": "Den här leverantören använder OAuth-autentisering från Gemini CLI-verktyget och kräver inga API-nycklar.",
+			"oauthPath": "OAuth-inloggningsuppgifter sökväg (valfritt)",
+			"oauthPathDescription": "Sökväg till OAuth-inloggningsuppgifter filen. Lämna tom för att använda standardplatsen (~/.gemini/oauth_creds.json).",
+			"instructions": "Om du inte har autentiserat än, kör",
+			"instructionsContinued": "i din terminal först.",
+			"setupLink": "Gemini CLI-installationsinstruktioner",
+			"requirementsTitle": "Viktiga krav",
+			"requirement1": "Först måste du installera Gemini CLI-verktyget",
+			"requirement2": "Sedan, kör gemini i din terminal och se till att du loggar in med Google",
+			"requirement3": "Fungerar endast med personliga Google-konton (inte Google Workspace-konton)",
+			"requirement4": "Använder inte API-nycklar - autentisering hanteras via OAuth",
+			"requirement5": "Kräver att Gemini CLI-verktyget installeras och autentiseras först",
+			"freeAccess": "Gratis nivå åtkomst via OAuth-autentisering"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/th/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "เส้นทาง Claude Code",
 			"description": "เส้นทางที่ไม่บังคับไปยัง Claude Code CLI ของคุณ ค่าเริ่มต้นคือ 'claude' หากไม่ได้ตั้งค่า",
 			"placeholder": "ค่าเริ่มต้น: claude"
+		},
+		"geminiCli": {
+			"description": "ผู้ให้บริการนี้ใช้การยืนยันตัวตน OAuth จากเครื่องมือ Gemini CLI และไม่ต้องการกุญแจ API",
+			"oauthPath": "เส้นทางข้อมูลประจำตัว OAuth (ไม่บังคับ)",
+			"oauthPathDescription": "เส้นทางไปยังไฟล์ข้อมูลประจำตัว OAuth ปล่อยว่างไว้เพื่อใช้ตำแหน่งเริ่มต้น (~/.gemini/oauth_creds.json)",
+			"instructions": "หากคุณยังไม่ได้ยืนยันตัวตน โปรดรัน",
+			"instructionsContinued": "ใน terminal ของคุณก่อน",
+			"setupLink": "คำแนะนำการตั้งค่า Gemini CLI",
+			"requirementsTitle": "ข้อกำหนดที่สำคัญ",
+			"requirement1": "ก่อนอื่น คุณต้องติดตั้งเครื่องมือ Gemini CLI",
+			"requirement2": "จากนั้น รัน gemini ใน terminal ของคุณและตรวจสอบให้แน่ใจว่าคุณเข้าสู่ระบบด้วย Google",
+			"requirement3": "ใช้งานได้เฉพาะกับบัญชี Google ส่วนตัว (ไม่ใช่บัญชี Google Workspace)",
+			"requirement4": "ไม่ใช้กุญแจ API - การยืนยันตัวตนจัดการผ่าน OAuth",
+			"requirement5": "ต้องการให้เครื่องมือ Gemini CLI ถูกติดตั้งและยืนยันตัวตนก่อน",
+			"freeAccess": "การเข้าถึงระดับฟรีผ่านการยืนยันตัวตน OAuth"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/tr/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Claude Code Yolu",
 			"description": "Claude Code CLI'nize isteğe bağlı yol. Ayarlanmazsa varsayılan olarak 'claude' kullanılır.",
 			"placeholder": "Varsayılan: claude"
+		},
+		"geminiCli": {
+			"description": "Bu sağlayıcı Gemini CLI aracının OAuth kimlik doğrulamasını kullanır ve API anahtarları gerektirmez.",
+			"oauthPath": "OAuth Kimlik Bilgileri Yolu (isteğe bağlı)",
+			"oauthPathDescription": "OAuth kimlik bilgileri dosyasının yolu. Varsayılan konumu (~/.gemini/oauth_creds.json) kullanmak için boş bırakın.",
+			"instructions": "Henüz kimlik doğrulama yapmadıysan, lütfen çalıştır",
+			"instructionsContinued": "terminalinde önce.",
+			"setupLink": "Gemini CLI Kurulum Talimatları",
+			"requirementsTitle": "Önemli Gereksinimler",
+			"requirement1": "Önce, Gemini CLI aracını yüklemelisin",
+			"requirement2": "Sonra, terminalinde gemini çalıştır ve Google ile giriş yaptığından emin ol",
+			"requirement3": "Sadece kişisel Google hesaplarıyla çalışır (Google Workspace hesapları değil)",
+			"requirement4": "API anahtarları kullanmaz - kimlik doğrulama OAuth aracılığıyla yönetilir",
+			"requirement5": "Gemini CLI aracının önce yüklenmesi ve kimlik doğrulaması yapılması gerektirir",
+			"freeAccess": "OAuth kimlik doğrulaması aracılığıyla ücretsiz katman erişimi"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/uk/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Шлях до Claude Code",
 			"description": "Необов'язковий шлях до вашого Claude Code CLI. За замовчуванням 'claude', якщо не встановлено.",
 			"placeholder": "За замовчуванням: claude"
+		},
+		"geminiCli": {
+			"description": "Цей провайдер використовує OAuth-аутентифікацію інструменту Gemini CLI і не потребує API-ключів.",
+			"oauthPath": "Шлях до облікових даних OAuth (необов'язково)",
+			"oauthPathDescription": "Шлях до файлу облікових даних OAuth. Залиште порожнім для використання місця за замовчуванням (~/.gemini/oauth_creds.json).",
+			"instructions": "Якщо ти ще не пройшов аутентифікацію, будь ласка, виконай",
+			"instructionsContinued": "у своєму терміналі спочатку.",
+			"setupLink": "Інструкції налаштування Gemini CLI",
+			"requirementsTitle": "Важливі вимоги",
+			"requirement1": "Спочатку тобі потрібно встановити інструмент Gemini CLI",
+			"requirement2": "Потім запусти gemini у своєму терміналі і переконайся, що увійшов через Google",
+			"requirement3": "Працює лише з особистими обліковими записами Google (не обліковими записами Google Workspace)",
+			"requirement4": "Не використовує API-ключі - аутентифікація здійснюється через OAuth",
+			"requirement5": "Потребує, щоб інструмент Gemini CLI був спочатку встановлений і аутентифікований",
+			"freeAccess": "Безкоштовний доступ через OAuth-аутентифікацію"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/vi/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Đường dẫn Claude Code",
 			"description": "Đường dẫn tùy chọn đến Claude Code CLI của bạn. Mặc định là 'claude' nếu không được đặt.",
 			"placeholder": "Mặc định: claude"
+		},
+		"geminiCli": {
+			"description": "Nhà cung cấp này sử dụng xác thực OAuth từ công cụ Gemini CLI và không cần khóa API.",
+			"oauthPath": "Đường dẫn thông tin xác thực OAuth (tùy chọn)",
+			"oauthPathDescription": "Đường dẫn đến tệp thông tin xác thực OAuth. Để trống để sử dụng vị trí mặc định (~/.gemini/oauth_creds.json).",
+			"instructions": "Nếu bạn chưa xác thực, vui lòng chạy",
+			"instructionsContinued": "trong terminal của bạn trước.",
+			"setupLink": "Hướng dẫn thiết lập Gemini CLI",
+			"requirementsTitle": "Yêu cầu quan trọng",
+			"requirement1": "Trước tiên, bạn cần cài đặt công cụ Gemini CLI",
+			"requirement2": "Sau đó, chạy gemini trong terminal và đảm bảo bạn đăng nhập bằng Google",
+			"requirement3": "Chỉ hoạt động với tài khoản Google cá nhân (không phải tài khoản Google Workspace)",
+			"requirement4": "Không sử dụng khóa API - xác thực được xử lý qua OAuth",
+			"requirement5": "Yêu cầu công cụ Gemini CLI phải được cài đặt và xác thực trước",
+			"freeAccess": "Truy cập cấp miễn phí qua xác thực OAuth"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/zh-CN/settings.json

@@ -318,6 +318,21 @@
 			"pathLabel": "Claude Code 路径",
 			"description": "您的 Claude Code CLI 的可选路径。如果未设置,则默认为 “claude”。",
 			"placeholder": "默认:claude"
+		},
+		"geminiCli": {
+			"description": "此提供商使用 Gemini CLI 工具的 OAuth 身份验证,不需要 API 密钥。",
+			"oauthPath": "OAuth 凭据路径(可选)",
+			"oauthPathDescription": "OAuth 凭据文件的路径。留空以使用默认位置 (~/.gemini/oauth_creds.json)。",
+			"instructions": "如果你还没有进行身份验证,请运行",
+			"instructionsContinued": "在你的终端中首先。",
+			"setupLink": "Gemini CLI 设置说明",
+			"requirementsTitle": "重要要求",
+			"requirement1": "首先,你需要安装 Gemini CLI 工具",
+			"requirement2": "然后,在你的终端中运行 gemini 并确保你用 Google 登录",
+			"requirement3": "仅适用于个人 Google 账户(不适用于 Google Workspace 账户)",
+			"requirement4": "不使用 API 密钥 - 身份验证通过 OAuth 处理",
+			"requirement5": "需要首先安装并验证 Gemini CLI 工具",
+			"freeAccess": "通过 OAuth 身份验证的免费层访问"
 		}
 	},
 	"browser": {

+ 15 - 0
webview-ui/src/i18n/locales/zh-TW/settings.json

@@ -317,6 +317,21 @@
 			"pathLabel": "Claude Code 路徑",
 			"description": "可選的 Claude Code CLI 路徑。如果未設定,則預設為 'claude'。",
 			"placeholder": "預設:claude"
+		},
+		"geminiCli": {
+			"description": "此提供商使用 Gemini CLI 工具的 OAuth 身份驗證,不需要 API 金鑰。",
+			"oauthPath": "OAuth 憑證路徑(可選)",
+			"oauthPathDescription": "OAuth 憑證檔案的路徑。留空以使用預設位置 (~/.gemini/oauth_creds.json)。",
+			"instructions": "如果你還沒有進行身份驗證,請執行",
+			"instructionsContinued": "在你的終端機中首先。",
+			"setupLink": "Gemini CLI 設定說明",
+			"requirementsTitle": "重要要求",
+			"requirement1": "首先,你需要安裝 Gemini CLI 工具",
+			"requirement2": "然後,在你的終端機中執行 gemini 並確保你用 Google 登入",
+			"requirement3": "僅適用於個人 Google 帳戶(不適用於 Google Workspace 帳戶)",
+			"requirement4": "不使用 API 金鑰 - 身份驗證透過 OAuth 處理",
+			"requirement5": "需要首先安裝並驗證 Gemini CLI 工具",
+			"freeAccess": "透過 OAuth 身份驗證的免費層存取"
 		}
 	},
 	"browser": {

+ 3 - 0
webview-ui/src/utils/validate.ts

@@ -72,6 +72,9 @@ function validateModelsAndKeysProvided(apiConfiguration: ProviderSettings): stri
 				return i18next.t("settings:validation.apiKey")
 			}
 			break
+		case "gemini-cli":
+			// OAuth-based provider, no API key validation needed
+			break
 		case "openai-native":
 			if (!apiConfiguration.openAiNativeApiKey) {
 				return i18next.t("settings:validation.apiKey")