Browse Source

Add support for Sonic model (#7207)

Co-authored-by: cte <[email protected]>
Matt Rubens 4 months ago
parent
commit
fd3535c21a
68 changed files with 739 additions and 66 deletions
  1. 7 0
      packages/types/src/provider-settings.ts
  2. 1 0
      packages/types/src/providers/index.ts
  3. 19 0
      packages/types/src/providers/roo.ts
  4. 9 9
      pnpm-lock.yaml
  5. 3 0
      src/api/index.ts
  6. 436 0
      src/api/providers/__tests__/roo.spec.ts
  7. 1 0
      src/api/providers/index.ts
  8. 53 0
      src/api/providers/roo.ts
  9. 3 0
      src/i18n/locales/ca/common.json
  10. 3 0
      src/i18n/locales/de/common.json
  11. 3 0
      src/i18n/locales/en/common.json
  12. 3 0
      src/i18n/locales/es/common.json
  13. 3 0
      src/i18n/locales/fr/common.json
  14. 3 0
      src/i18n/locales/hi/common.json
  15. 3 0
      src/i18n/locales/id/common.json
  16. 3 0
      src/i18n/locales/it/common.json
  17. 3 0
      src/i18n/locales/ja/common.json
  18. 3 0
      src/i18n/locales/ko/common.json
  19. 3 0
      src/i18n/locales/nl/common.json
  20. 3 0
      src/i18n/locales/pl/common.json
  21. 3 0
      src/i18n/locales/pt-BR/common.json
  22. 3 0
      src/i18n/locales/ru/common.json
  23. 3 0
      src/i18n/locales/tr/common.json
  24. 3 0
      src/i18n/locales/vi/common.json
  25. 3 0
      src/i18n/locales/zh-CN/common.json
  26. 3 0
      src/i18n/locales/zh-TW/common.json
  27. 1 1
      src/package.json
  28. 23 2
      webview-ui/src/components/settings/ApiOptions.tsx
  29. 1 1
      webview-ui/src/components/settings/ModelDescriptionMarkdown.tsx
  30. 13 17
      webview-ui/src/components/settings/ModelInfoView.tsx
  31. 3 0
      webview-ui/src/components/settings/constants.ts
  32. 7 0
      webview-ui/src/components/ui/hooks/useSelectedModel.ts
  33. 2 2
      webview-ui/src/i18n/locales/ca/chat.json
  34. 4 0
      webview-ui/src/i18n/locales/ca/settings.json
  35. 2 2
      webview-ui/src/i18n/locales/de/chat.json
  36. 4 0
      webview-ui/src/i18n/locales/de/settings.json
  37. 2 2
      webview-ui/src/i18n/locales/en/chat.json
  38. 4 0
      webview-ui/src/i18n/locales/en/settings.json
  39. 2 2
      webview-ui/src/i18n/locales/es/chat.json
  40. 4 0
      webview-ui/src/i18n/locales/es/settings.json
  41. 2 2
      webview-ui/src/i18n/locales/fr/chat.json
  42. 4 0
      webview-ui/src/i18n/locales/fr/settings.json
  43. 2 2
      webview-ui/src/i18n/locales/hi/chat.json
  44. 4 0
      webview-ui/src/i18n/locales/hi/settings.json
  45. 2 2
      webview-ui/src/i18n/locales/id/chat.json
  46. 4 0
      webview-ui/src/i18n/locales/id/settings.json
  47. 2 2
      webview-ui/src/i18n/locales/it/chat.json
  48. 4 0
      webview-ui/src/i18n/locales/it/settings.json
  49. 2 2
      webview-ui/src/i18n/locales/ja/chat.json
  50. 4 0
      webview-ui/src/i18n/locales/ja/settings.json
  51. 2 2
      webview-ui/src/i18n/locales/ko/chat.json
  52. 4 0
      webview-ui/src/i18n/locales/ko/settings.json
  53. 2 2
      webview-ui/src/i18n/locales/nl/chat.json
  54. 4 0
      webview-ui/src/i18n/locales/nl/settings.json
  55. 2 2
      webview-ui/src/i18n/locales/pl/chat.json
  56. 4 0
      webview-ui/src/i18n/locales/pl/settings.json
  57. 2 2
      webview-ui/src/i18n/locales/pt-BR/chat.json
  58. 4 0
      webview-ui/src/i18n/locales/pt-BR/settings.json
  59. 2 2
      webview-ui/src/i18n/locales/ru/chat.json
  60. 4 0
      webview-ui/src/i18n/locales/ru/settings.json
  61. 2 2
      webview-ui/src/i18n/locales/tr/chat.json
  62. 4 0
      webview-ui/src/i18n/locales/tr/settings.json
  63. 2 2
      webview-ui/src/i18n/locales/vi/chat.json
  64. 4 0
      webview-ui/src/i18n/locales/vi/settings.json
  65. 2 2
      webview-ui/src/i18n/locales/zh-CN/chat.json
  66. 4 0
      webview-ui/src/i18n/locales/zh-CN/settings.json
  67. 2 2
      webview-ui/src/i18n/locales/zh-TW/chat.json
  68. 4 0
      webview-ui/src/i18n/locales/zh-TW/settings.json

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

@@ -47,6 +47,7 @@ export const providerNames = [
 	"zai",
 	"fireworks",
 	"io-intelligence",
+	"roo",
 ] as const
 
 export const providerNamesSchema = z.enum(providerNames)
@@ -288,6 +289,10 @@ const ioIntelligenceSchema = apiModelIdProviderModelSchema.extend({
 	ioIntelligenceApiKey: z.string().optional(),
 })
 
+const rooSchema = apiModelIdProviderModelSchema.extend({
+	// No additional fields needed - uses cloud authentication
+})
+
 const defaultSchema = z.object({
 	apiProvider: z.undefined(),
 })
@@ -324,6 +329,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
 	zaiSchema.merge(z.object({ apiProvider: z.literal("zai") })),
 	fireworksSchema.merge(z.object({ apiProvider: z.literal("fireworks") })),
 	ioIntelligenceSchema.merge(z.object({ apiProvider: z.literal("io-intelligence") })),
+	rooSchema.merge(z.object({ apiProvider: z.literal("roo") })),
 	defaultSchema,
 ])
 
@@ -360,6 +366,7 @@ export const providerSettingsSchema = z.object({
 	...zaiSchema.shape,
 	...fireworksSchema.shape,
 	...ioIntelligenceSchema.shape,
+	...rooSchema.shape,
 	...codebaseIndexProviderSchema.shape,
 })
 

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

@@ -25,3 +25,4 @@ export * from "./xai.js"
 export * from "./doubao.js"
 export * from "./zai.js"
 export * from "./fireworks.js"
+export * from "./roo.js"

+ 19 - 0
packages/types/src/providers/roo.ts

@@ -0,0 +1,19 @@
+import type { ModelInfo } from "../model.js"
+
+// Roo provider with single model
+export type RooModelId = "roo/sonic"
+
+export const rooDefaultModelId: RooModelId = "roo/sonic"
+
+export const rooModels = {
+	"roo/sonic": {
+		maxTokens: 8192,
+		contextWindow: 262_144,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description:
+			"Stealth coding model with 262K context window, accessible for free through Roo Code Cloud for a limited time. (Note: prompts and completions are logged by the model creator and used to improve the model.)",
+	},
+} as const satisfies Record<string, ModelInfo>

+ 9 - 9
pnpm-lock.yaml

@@ -584,8 +584,8 @@ importers:
         specifier: ^1.14.0
         version: 1.14.0([email protected])
       '@roo-code/cloud':
-        specifier: ^0.17.0
-        version: 0.17.0
+        specifier: ^0.18.0
+        version: 0.18.0
       '@roo-code/ipc':
         specifier: workspace:^
         version: link:../packages/ipc
@@ -3106,11 +3106,11 @@ packages:
     cpu: [x64]
     os: [win32]
 
-  '@roo-code/[email protected]7.0':
-    resolution: {integrity: sha512-Sh7KGbVapxocnoyDzWkvvtVzam5jZnU2RGKT7V0v3CbFMQc329NBnHRAgVtJUwcGPHUFyAkwEHo181Fn3rFpZw==}
+  '@roo-code/[email protected]8.0':
+    resolution: {integrity: sha512-Y2jbcUVB9RCQFAxHDPrfjWQU1o7yRvWaPAdA3eZjsUf+zfDL59Rwfghg6loqDfE/8HCkcJmHfLCKovNX5ju5qA==}
 
-  '@roo-code/[email protected]2.0':
-    resolution: {integrity: sha512-jPCVZ2j4Y0MUiHvAJbXduR3yFGb5A3KDvIThi8cKBAUg8zb7jhIYKTvJ5vub1MGZkpK11K0JG6ex4RL19FjobA==}
+  '@roo-code/[email protected]4.0':
+    resolution: {integrity: sha512-Xj3Zn2FhXbG2bpwXuhrjKnkeuWypQCIPKljOLXnOCUqaMUhP1zkWwNZ+I3gIBUpDng/iWN3KHon1if0UaoXYQw==}
 
   '@sec-ant/[email protected]':
     resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
@@ -12314,9 +12314,9 @@ snapshots:
   '@rollup/[email protected]':
     optional: true
 
-  '@roo-code/[email protected]7.0':
+  '@roo-code/[email protected]8.0':
     dependencies:
-      '@roo-code/types': 1.52.0
+      '@roo-code/types': 1.54.0
       ioredis: 5.6.1
       p-wait-for: 5.0.2
       socket.io-client: 4.8.1
@@ -12326,7 +12326,7 @@ snapshots:
       - supports-color
       - utf-8-validate
 
-  '@roo-code/[email protected]2.0':
+  '@roo-code/[email protected]4.0':
     dependencies:
       zod: 3.25.76
 

+ 3 - 0
src/api/index.ts

@@ -35,6 +35,7 @@ import {
 	DoubaoHandler,
 	ZAiHandler,
 	FireworksHandler,
+	RooHandler,
 } from "./providers"
 import { NativeOllamaHandler } from "./providers/native-ollama"
 
@@ -140,6 +141,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
 			return new FireworksHandler(options)
 		case "io-intelligence":
 			return new IOIntelligenceHandler(options)
+		case "roo":
+			return new RooHandler(options)
 		default:
 			apiProvider satisfies "gemini-cli" | undefined
 			return new AnthropicHandler(options)

+ 436 - 0
src/api/providers/__tests__/roo.spec.ts

@@ -0,0 +1,436 @@
+// npx vitest run api/providers/__tests__/roo.spec.ts
+
+import { Anthropic } from "@anthropic-ai/sdk"
+import { rooDefaultModelId, rooModels } from "@roo-code/types"
+
+import { ApiHandlerOptions } from "../../../shared/api"
+
+// Mock OpenAI client
+const mockCreate = vitest.fn()
+
+vitest.mock("openai", () => {
+	return {
+		__esModule: true,
+		default: vitest.fn().mockImplementation(() => ({
+			chat: {
+				completions: {
+					create: mockCreate.mockImplementation(async (options) => {
+						if (!options.stream) {
+							return {
+								id: "test-completion",
+								choices: [
+									{
+										message: { role: "assistant", content: "Test response" },
+										finish_reason: "stop",
+										index: 0,
+									},
+								],
+								usage: {
+									prompt_tokens: 10,
+									completion_tokens: 5,
+									total_tokens: 15,
+								},
+							}
+						}
+
+						return {
+							[Symbol.asyncIterator]: async function* () {
+								yield {
+									choices: [
+										{
+											delta: { content: "Test response" },
+											index: 0,
+										},
+									],
+									usage: null,
+								}
+								yield {
+									choices: [
+										{
+											delta: {},
+											index: 0,
+										},
+									],
+									usage: {
+										prompt_tokens: 10,
+										completion_tokens: 5,
+										total_tokens: 15,
+									},
+								}
+							},
+						}
+					}),
+				},
+			},
+		})),
+	}
+})
+
+// Mock CloudService - Define functions outside to avoid initialization issues
+const mockGetSessionToken = vitest.fn()
+const mockHasInstance = vitest.fn()
+
+// Create mock functions that we can control
+const mockGetSessionTokenFn = vitest.fn()
+const mockHasInstanceFn = vitest.fn()
+
+vitest.mock("@roo-code/cloud", () => ({
+	CloudService: {
+		hasInstance: () => mockHasInstanceFn(),
+		get instance() {
+			return {
+				authService: {
+					getSessionToken: () => mockGetSessionTokenFn(),
+				},
+			}
+		},
+	},
+}))
+
+// Mock i18n
+vitest.mock("../../../i18n", () => ({
+	t: vitest.fn((key: string) => {
+		if (key === "common:errors.roo.authenticationRequired") {
+			return "Authentication required for Roo Code Cloud"
+		}
+		return key
+	}),
+}))
+
+// Import after mocks are set up
+import { RooHandler } from "../roo"
+import { CloudService } from "@roo-code/cloud"
+import { t } from "../../../i18n"
+
+describe("RooHandler", () => {
+	let handler: RooHandler
+	let mockOptions: ApiHandlerOptions
+	const systemPrompt = "You are a helpful assistant."
+	const messages: Anthropic.Messages.MessageParam[] = [
+		{
+			role: "user",
+			content: "Hello!",
+		},
+	]
+
+	beforeEach(() => {
+		mockOptions = {
+			apiModelId: "roo/sonic",
+		}
+		// Set up CloudService mocks for successful authentication
+		mockHasInstanceFn.mockReturnValue(true)
+		mockGetSessionTokenFn.mockReturnValue("test-session-token")
+		mockCreate.mockClear()
+		vitest.clearAllMocks()
+	})
+
+	describe("constructor", () => {
+		it("should initialize with valid session token", () => {
+			handler = new RooHandler(mockOptions)
+			expect(handler).toBeInstanceOf(RooHandler)
+			expect(handler.getModel().id).toBe(mockOptions.apiModelId)
+		})
+
+		it("should throw error if CloudService is not available", () => {
+			mockHasInstanceFn.mockReturnValue(false)
+			expect(() => {
+				new RooHandler(mockOptions)
+			}).toThrow("Authentication required for Roo Code Cloud")
+			expect(t).toHaveBeenCalledWith("common:errors.roo.authenticationRequired")
+		})
+
+		it("should throw error if session token is not available", () => {
+			mockHasInstanceFn.mockReturnValue(true)
+			mockGetSessionTokenFn.mockReturnValue(null)
+			expect(() => {
+				new RooHandler(mockOptions)
+			}).toThrow("Authentication required for Roo Code Cloud")
+			expect(t).toHaveBeenCalledWith("common:errors.roo.authenticationRequired")
+		})
+
+		it("should initialize with default model if no model specified", () => {
+			handler = new RooHandler({})
+			expect(handler).toBeInstanceOf(RooHandler)
+			expect(handler.getModel().id).toBe(rooDefaultModelId)
+		})
+
+		it("should pass correct configuration to base class", () => {
+			handler = new RooHandler(mockOptions)
+			expect(handler).toBeInstanceOf(RooHandler)
+			// The handler should be initialized with correct base URL and API key
+			// We can't directly test the parent class constructor, but we can verify the handler works
+			expect(handler).toBeDefined()
+		})
+	})
+
+	describe("createMessage", () => {
+		beforeEach(() => {
+			handler = new RooHandler(mockOptions)
+		})
+
+		it("should handle streaming responses", async () => {
+			const stream = handler.createMessage(systemPrompt, messages)
+			const chunks: any[] = []
+			for await (const chunk of stream) {
+				chunks.push(chunk)
+			}
+
+			expect(chunks.length).toBeGreaterThan(0)
+			const textChunks = chunks.filter((chunk) => chunk.type === "text")
+			expect(textChunks).toHaveLength(1)
+			expect(textChunks[0].text).toBe("Test response")
+		})
+
+		it("should include usage information", async () => {
+			const stream = handler.createMessage(systemPrompt, messages)
+			const chunks: any[] = []
+			for await (const chunk of stream) {
+				chunks.push(chunk)
+			}
+
+			const usageChunks = chunks.filter((chunk) => chunk.type === "usage")
+			expect(usageChunks).toHaveLength(1)
+			expect(usageChunks[0].inputTokens).toBe(10)
+			expect(usageChunks[0].outputTokens).toBe(5)
+		})
+
+		it("should handle API errors", async () => {
+			mockCreate.mockRejectedValueOnce(new Error("API Error"))
+			const stream = handler.createMessage(systemPrompt, messages)
+			await expect(async () => {
+				for await (const _chunk of stream) {
+					// Should not reach here
+				}
+			}).rejects.toThrow("API Error")
+		})
+
+		it("should handle empty response content", async () => {
+			mockCreate.mockResolvedValueOnce({
+				[Symbol.asyncIterator]: async function* () {
+					yield {
+						choices: [
+							{
+								delta: { content: null },
+								index: 0,
+							},
+						],
+						usage: {
+							prompt_tokens: 10,
+							completion_tokens: 0,
+							total_tokens: 10,
+						},
+					}
+				},
+			})
+
+			const stream = handler.createMessage(systemPrompt, messages)
+			const chunks: any[] = []
+			for await (const chunk of stream) {
+				chunks.push(chunk)
+			}
+
+			const textChunks = chunks.filter((chunk) => chunk.type === "text")
+			expect(textChunks).toHaveLength(0)
+			const usageChunks = chunks.filter((chunk) => chunk.type === "usage")
+			expect(usageChunks).toHaveLength(1)
+		})
+
+		it("should handle multiple messages in conversation", async () => {
+			const multipleMessages: Anthropic.Messages.MessageParam[] = [
+				{ role: "user", content: "First message" },
+				{ role: "assistant", content: "First response" },
+				{ role: "user", content: "Second message" },
+			]
+
+			const stream = handler.createMessage(systemPrompt, multipleMessages)
+			const chunks: any[] = []
+			for await (const chunk of stream) {
+				chunks.push(chunk)
+			}
+
+			expect(mockCreate).toHaveBeenCalledWith(
+				expect.objectContaining({
+					messages: expect.arrayContaining([
+						expect.objectContaining({ role: "system", content: systemPrompt }),
+						expect.objectContaining({ role: "user", content: "First message" }),
+						expect.objectContaining({ role: "assistant", content: "First response" }),
+						expect.objectContaining({ role: "user", content: "Second message" }),
+					]),
+				}),
+			)
+		})
+	})
+
+	describe("completePrompt", () => {
+		beforeEach(() => {
+			handler = new RooHandler(mockOptions)
+		})
+
+		it("should complete prompt successfully", async () => {
+			const result = await handler.completePrompt("Test prompt")
+			expect(result).toBe("Test response")
+			expect(mockCreate).toHaveBeenCalledWith({
+				model: mockOptions.apiModelId,
+				messages: [{ role: "user", content: "Test prompt" }],
+			})
+		})
+
+		it("should handle API errors", async () => {
+			mockCreate.mockRejectedValueOnce(new Error("API Error"))
+			await expect(handler.completePrompt("Test prompt")).rejects.toThrow(
+				"Roo Code Cloud completion error: API Error",
+			)
+		})
+
+		it("should handle empty response", async () => {
+			mockCreate.mockResolvedValueOnce({
+				choices: [{ message: { content: "" } }],
+			})
+			const result = await handler.completePrompt("Test prompt")
+			expect(result).toBe("")
+		})
+
+		it("should handle missing response content", async () => {
+			mockCreate.mockResolvedValueOnce({
+				choices: [{ message: {} }],
+			})
+			const result = await handler.completePrompt("Test prompt")
+			expect(result).toBe("")
+		})
+	})
+
+	describe("getModel", () => {
+		beforeEach(() => {
+			handler = new RooHandler(mockOptions)
+		})
+
+		it("should return model info for specified model", () => {
+			const modelInfo = handler.getModel()
+			expect(modelInfo.id).toBe(mockOptions.apiModelId)
+			expect(modelInfo.info).toBeDefined()
+			// roo/sonic is a valid model in rooModels
+			expect(modelInfo.info).toBe(rooModels["roo/sonic"])
+		})
+
+		it("should return default model when no model specified", () => {
+			const handlerWithoutModel = new RooHandler({})
+			const modelInfo = handlerWithoutModel.getModel()
+			expect(modelInfo.id).toBe(rooDefaultModelId)
+			expect(modelInfo.info).toBeDefined()
+			expect(modelInfo.info).toBe(rooModels[rooDefaultModelId])
+		})
+
+		it("should handle unknown model ID with fallback info", () => {
+			const handlerWithUnknownModel = new RooHandler({
+				apiModelId: "unknown-model-id",
+			})
+			const modelInfo = handlerWithUnknownModel.getModel()
+			expect(modelInfo.id).toBe("unknown-model-id")
+			expect(modelInfo.info).toBeDefined()
+			// Should return fallback info for unknown models
+			expect(modelInfo.info.maxTokens).toBe(8192)
+			expect(modelInfo.info.contextWindow).toBe(262_144)
+			expect(modelInfo.info.supportsImages).toBe(false)
+			expect(modelInfo.info.supportsPromptCache).toBe(false)
+			expect(modelInfo.info.inputPrice).toBe(0)
+			expect(modelInfo.info.outputPrice).toBe(0)
+		})
+
+		it("should return correct model info for all Roo models", () => {
+			// Test each model in rooModels
+			const modelIds = Object.keys(rooModels) as Array<keyof typeof rooModels>
+
+			for (const modelId of modelIds) {
+				const handlerWithModel = new RooHandler({ apiModelId: modelId })
+				const modelInfo = handlerWithModel.getModel()
+				expect(modelInfo.id).toBe(modelId)
+				expect(modelInfo.info).toBe(rooModels[modelId])
+			}
+		})
+	})
+
+	describe("temperature and model configuration", () => {
+		it("should use default temperature of 0.7", async () => {
+			handler = new RooHandler(mockOptions)
+			const stream = handler.createMessage(systemPrompt, messages)
+			for await (const _chunk of stream) {
+				// Consume stream
+			}
+
+			expect(mockCreate).toHaveBeenCalledWith(
+				expect.objectContaining({
+					temperature: 0.7,
+				}),
+			)
+		})
+
+		it("should respect custom temperature setting", async () => {
+			handler = new RooHandler({
+				...mockOptions,
+				modelTemperature: 0.9,
+			})
+			const stream = handler.createMessage(systemPrompt, messages)
+			for await (const _chunk of stream) {
+				// Consume stream
+			}
+
+			expect(mockCreate).toHaveBeenCalledWith(
+				expect.objectContaining({
+					temperature: 0.9,
+				}),
+			)
+		})
+
+		it("should use correct API endpoint", () => {
+			// The base URL should be set to Roo's API endpoint
+			// We can't directly test the OpenAI client configuration, but we can verify the handler initializes
+			handler = new RooHandler(mockOptions)
+			expect(handler).toBeInstanceOf(RooHandler)
+			// The handler should work with the Roo API endpoint
+		})
+	})
+
+	describe("authentication flow", () => {
+		it("should use session token as API key", () => {
+			const testToken = "test-session-token-123"
+			mockGetSessionTokenFn.mockReturnValue(testToken)
+
+			handler = new RooHandler(mockOptions)
+			expect(handler).toBeInstanceOf(RooHandler)
+			expect(mockGetSessionTokenFn).toHaveBeenCalled()
+		})
+
+		it("should handle undefined auth service", () => {
+			mockHasInstanceFn.mockReturnValue(true)
+			// Mock CloudService with undefined authService
+			const originalGetter = Object.getOwnPropertyDescriptor(CloudService, "instance")?.get
+
+			try {
+				Object.defineProperty(CloudService, "instance", {
+					get: () => ({ authService: undefined }),
+					configurable: true,
+				})
+
+				expect(() => {
+					new RooHandler(mockOptions)
+				}).toThrow("Authentication required for Roo Code Cloud")
+			} finally {
+				// Always restore original getter, even if test fails
+				if (originalGetter) {
+					Object.defineProperty(CloudService, "instance", {
+						get: originalGetter,
+						configurable: true,
+					})
+				}
+			}
+		})
+
+		it("should handle empty session token", () => {
+			mockGetSessionTokenFn.mockReturnValue("")
+
+			expect(() => {
+				new RooHandler(mockOptions)
+			}).toThrow("Authentication required for Roo Code Cloud")
+		})
+	})
+})

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

@@ -29,3 +29,4 @@ export { VsCodeLmHandler } from "./vscode-lm"
 export { XAIHandler } from "./xai"
 export { ZAiHandler } from "./zai"
 export { FireworksHandler } from "./fireworks"
+export { RooHandler } from "./roo"

+ 53 - 0
src/api/providers/roo.ts

@@ -0,0 +1,53 @@
+import { rooDefaultModelId, rooModels, type RooModelId } from "@roo-code/types"
+import { CloudService } from "@roo-code/cloud"
+
+import type { ApiHandlerOptions } from "../../shared/api"
+import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"
+import { t } from "../../i18n"
+
+export class RooHandler extends BaseOpenAiCompatibleProvider<RooModelId> {
+	constructor(options: ApiHandlerOptions) {
+		// Check if CloudService is available and get the session token.
+		if (!CloudService.hasInstance()) {
+			throw new Error(t("common:errors.roo.authenticationRequired"))
+		}
+
+		const sessionToken = CloudService.instance.authService?.getSessionToken()
+
+		if (!sessionToken) {
+			throw new Error(t("common:errors.roo.authenticationRequired"))
+		}
+
+		super({
+			...options,
+			providerName: "Roo Code Cloud",
+			baseURL: "https://api.roocode.com/proxy/v1",
+			apiKey: sessionToken,
+			defaultProviderModelId: rooDefaultModelId,
+			providerModels: rooModels,
+			defaultTemperature: 0.7,
+		})
+	}
+
+	override getModel() {
+		const modelId = this.options.apiModelId || rooDefaultModelId
+		const modelInfo = this.providerModels[modelId as RooModelId] ?? this.providerModels[rooDefaultModelId]
+
+		if (modelInfo) {
+			return { id: modelId as RooModelId, info: modelInfo }
+		}
+
+		// Return the requested model ID even if not found, with fallback info.
+		return {
+			id: modelId as RooModelId,
+			info: {
+				maxTokens: 8192,
+				contextWindow: 262_144,
+				supportsImages: false,
+				supportsPromptCache: false,
+				inputPrice: 0,
+				outputPrice: 0,
+			},
+		}
+	}
+}

+ 3 - 0
src/i18n/locales/ca/common.json

@@ -104,6 +104,9 @@
 			"noResponseBody": "Error de l'API de Cerebras: No hi ha cos de resposta",
 			"completionError": "Error de finalització de Cerebras: {{error}}"
 		},
+		"roo": {
+			"authenticationRequired": "El proveïdor Roo requereix autenticació al núvol. Si us plau, inicieu sessió a Roo Code Cloud."
+		},
 		"mode_import_failed": "Ha fallat la importació del mode: {{error}}"
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/de/common.json

@@ -100,6 +100,9 @@
 			"genericError": "Cerebras API-Fehler ({{status}}): {{message}}",
 			"noResponseBody": "Cerebras API-Fehler: Kein Antworttext vorhanden",
 			"completionError": "Cerebras-Vervollständigungsfehler: {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "Roo-Anbieter erfordert Cloud-Authentifizierung. Bitte melde dich bei Roo Code Cloud an."
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/en/common.json

@@ -100,6 +100,9 @@
 			"genericError": "Cerebras API Error ({{status}}): {{message}}",
 			"noResponseBody": "Cerebras API Error: No response body",
 			"completionError": "Cerebras completion error: {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "Roo provider requires cloud authentication. Please sign in to Roo Code Cloud."
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/es/common.json

@@ -100,6 +100,9 @@
 			"genericError": "Error de la API de Cerebras ({{status}}): {{message}}",
 			"noResponseBody": "Error de la API de Cerebras: Sin cuerpo de respuesta",
 			"completionError": "Error de finalización de Cerebras: {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "El proveedor Roo requiere autenticación en la nube. Por favor, inicia sesión en Roo Code Cloud."
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/fr/common.json

@@ -100,6 +100,9 @@
 			"genericError": "Erreur de l'API Cerebras ({{status}}) : {{message}}",
 			"noResponseBody": "Erreur de l'API Cerebras : Aucun corps de réponse",
 			"completionError": "Erreur d'achèvement de Cerebras : {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "Le fournisseur Roo nécessite une authentification cloud. Veuillez vous connecter à Roo Code Cloud."
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/hi/common.json

@@ -100,6 +100,9 @@
 			"genericError": "Cerebras API त्रुटि ({{status}}): {{message}}",
 			"noResponseBody": "Cerebras API त्रुटि: कोई प्रतिक्रिया मुख्य भाग नहीं",
 			"completionError": "Cerebras पूर्णता त्रुटि: {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "Roo प्रदाता को क्लाउड प्रमाणीकरण की आवश्यकता है। कृपया Roo Code Cloud में साइन इन करें।"
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/id/common.json

@@ -100,6 +100,9 @@
 			"genericError": "Kesalahan API Cerebras ({{status}}): {{message}}",
 			"noResponseBody": "Kesalahan API Cerebras: Tidak ada isi respons",
 			"completionError": "Kesalahan penyelesaian Cerebras: {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "Penyedia Roo memerlukan autentikasi cloud. Silakan masuk ke Roo Code Cloud."
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/it/common.json

@@ -100,6 +100,9 @@
 			"genericError": "Errore API Cerebras ({{status}}): {{message}}",
 			"noResponseBody": "Errore API Cerebras: Nessun corpo di risposta",
 			"completionError": "Errore di completamento Cerebras: {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "Il provider Roo richiede l'autenticazione cloud. Accedi a Roo Code Cloud."
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/ja/common.json

@@ -100,6 +100,9 @@
 			"genericError": "Cerebras APIエラー ({{status}}): {{message}}",
 			"noResponseBody": "Cerebras APIエラー: レスポンスボディなし",
 			"completionError": "Cerebras完了エラー: {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "Rooプロバイダーはクラウド認証が必要です。Roo Code Cloudにサインインしてください。"
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/ko/common.json

@@ -100,6 +100,9 @@
 			"genericError": "Cerebras API 오류 ({{status}}): {{message}}",
 			"noResponseBody": "Cerebras API 오류: 응답 본문 없음",
 			"completionError": "Cerebras 완료 오류: {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "Roo 제공업체는 클라우드 인증이 필요합니다. Roo Code Cloud에 로그인하세요."
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/nl/common.json

@@ -100,6 +100,9 @@
 			"genericError": "Cerebras API-fout ({{status}}): {{message}}",
 			"noResponseBody": "Cerebras API-fout: Geen responslichaam",
 			"completionError": "Cerebras-voltooiingsfout: {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "Roo provider vereist cloud authenticatie. Log in bij Roo Code Cloud."
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/pl/common.json

@@ -100,6 +100,9 @@
 			"genericError": "Błąd API Cerebras ({{status}}): {{message}}",
 			"noResponseBody": "Błąd API Cerebras: Brak treści odpowiedzi",
 			"completionError": "Błąd uzupełniania Cerebras: {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "Dostawca Roo wymaga uwierzytelnienia w chmurze. Zaloguj się do Roo Code Cloud."
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/pt-BR/common.json

@@ -104,6 +104,9 @@
 			"genericError": "Erro da API Cerebras ({{status}}): {{message}}",
 			"noResponseBody": "Erro da API Cerebras: Sem corpo de resposta",
 			"completionError": "Erro de conclusão do Cerebras: {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "O provedor Roo requer autenticação na nuvem. Faça login no Roo Code Cloud."
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/ru/common.json

@@ -100,6 +100,9 @@
 			"genericError": "Ошибка Cerebras API ({{status}}): {{message}}",
 			"noResponseBody": "Ошибка Cerebras API: Нет тела ответа",
 			"completionError": "Ошибка завершения Cerebras: {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "Провайдер Roo требует облачной аутентификации. Войдите в Roo Code Cloud."
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/tr/common.json

@@ -100,6 +100,9 @@
 			"genericError": "Cerebras API Hatası ({{status}}): {{message}}",
 			"noResponseBody": "Cerebras API Hatası: Yanıt gövdesi yok",
 			"completionError": "Cerebras tamamlama hatası: {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "Roo sağlayıcısı bulut kimlik doğrulaması gerektirir. Lütfen Roo Code Cloud'a giriş yapın."
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/vi/common.json

@@ -100,6 +100,9 @@
 			"genericError": "Lỗi API Cerebras ({{status}}): {{message}}",
 			"noResponseBody": "Lỗi API Cerebras: Không có nội dung phản hồi",
 			"completionError": "Lỗi hoàn thành Cerebras: {{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "Nhà cung cấp Roo yêu cầu xác thực đám mây. Vui lòng đăng nhập vào Roo Code Cloud."
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/zh-CN/common.json

@@ -105,6 +105,9 @@
 			"genericError": "Cerebras API 错误 ({{status}}):{{message}}",
 			"noResponseBody": "Cerebras API 错误:无响应主体",
 			"completionError": "Cerebras 完成错误:{{error}}"
+		},
+		"roo": {
+			"authenticationRequired": "Roo 提供商需要云认证。请登录 Roo Code Cloud。"
 		}
 	},
 	"warnings": {

+ 3 - 0
src/i18n/locales/zh-TW/common.json

@@ -100,6 +100,9 @@
 			"noResponseBody": "Cerebras API 錯誤:無回應主體",
 			"completionError": "Cerebras 完成錯誤:{{error}}"
 		},
+		"roo": {
+			"authenticationRequired": "Roo 提供者需要雲端認證。請登入 Roo Code Cloud。"
+		},
 		"mode_import_failed": "匯入模式失敗:{{error}}"
 	},
 	"warnings": {

+ 1 - 1
src/package.json

@@ -427,7 +427,7 @@
 		"@mistralai/mistralai": "^1.3.6",
 		"@modelcontextprotocol/sdk": "^1.9.0",
 		"@qdrant/js-client-rest": "^1.14.0",
-		"@roo-code/cloud": "^0.17.0",
+		"@roo-code/cloud": "^0.18.0",
 		"@roo-code/ipc": "workspace:^",
 		"@roo-code/telemetry": "workspace:^",
 		"@roo-code/types": "workspace:^",

+ 23 - 2
webview-ui/src/components/settings/ApiOptions.tsx

@@ -1,7 +1,7 @@
 import React, { memo, useCallback, useEffect, useMemo, useState } from "react"
 import { convertHeadersToObject } from "./utils/headers"
 import { useDebounce } from "react-use"
-import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
+import { VSCodeLink, VSCodeButton } from "@vscode/webview-ui-toolkit/react"
 import { ExternalLinkIcon } from "@radix-ui/react-icons"
 
 import {
@@ -32,6 +32,7 @@ import {
 	mainlandZAiDefaultModelId,
 	fireworksDefaultModelId,
 	ioIntelligenceDefaultModelId,
+	rooDefaultModelId,
 } from "@roo-code/types"
 
 import { vscode } from "@src/utils/vscode"
@@ -124,7 +125,7 @@ const ApiOptions = ({
 	setErrorMessage,
 }: ApiOptionsProps) => {
 	const { t } = useAppTranslation()
-	const { organizationAllowList } = useExtensionState()
+	const { organizationAllowList, cloudIsAuthenticated } = useExtensionState()
 
 	const [customHeaders, setCustomHeaders] = useState<[string, string][]>(() => {
 		const headers = apiConfiguration?.openAiHeaders || {}
@@ -327,6 +328,7 @@ const ApiOptions = ({
 				},
 				fireworks: { field: "apiModelId", default: fireworksDefaultModelId },
 				"io-intelligence": { field: "ioIntelligenceModelId", default: ioIntelligenceDefaultModelId },
+				roo: { field: "apiModelId", default: rooDefaultModelId },
 				openai: { field: "openAiModelId" },
 				ollama: { field: "ollamaModelId" },
 				lmstudio: { field: "lmStudioModelId" },
@@ -579,6 +581,25 @@ const ApiOptions = ({
 				<Fireworks apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
 			)}
 
+			{selectedProvider === "roo" && (
+				<div className="flex flex-col gap-3">
+					{cloudIsAuthenticated ? (
+						<div className="text-sm text-vscode-descriptionForeground">
+							{t("settings:providers.roo.authenticatedMessage")}
+						</div>
+					) : (
+						<div className="flex flex-col gap-2">
+							<VSCodeButton
+								appearance="primary"
+								onClick={() => vscode.postMessage({ type: "rooCloudSignIn" })}
+								className="w-fit">
+								{t("settings:providers.roo.connectButton")}
+							</VSCodeButton>
+						</div>
+					)}
+				</div>
+			)}
+
 			{selectedProviderModels.length > 0 && (
 				<>
 					<div>

+ 1 - 1
webview-ui/src/components/settings/ModelDescriptionMarkdown.tsx

@@ -34,7 +34,7 @@ export const ModelDescriptionMarkdown = memo(
 
 		return (
 			<Collapsible open={isExpanded} onOpenChange={setIsExpanded} className="relative">
-				<div ref={textContainerRef} className={cn({ "line-clamp-3": !isExpanded })}>
+				<div ref={textContainerRef} className={cn({ "line-clamp-4": !isExpanded })}>
 					<div ref={textRef}>
 						<StyledMarkdown key={key}>{content}</StyledMarkdown>
 					</div>

+ 13 - 17
webview-ui/src/components/settings/ModelInfoView.tsx

@@ -26,6 +26,18 @@ export const ModelInfoView = ({
 	const { t } = useAppTranslation()
 
 	const infoItems = [
+		typeof modelInfo?.contextWindow === "number" && modelInfo.contextWindow > 0 && (
+			<>
+				<span className="font-medium">{t("settings:modelInfo.contextWindow")}</span>{" "}
+				{modelInfo.contextWindow?.toLocaleString()} tokens
+			</>
+		),
+		typeof modelInfo?.maxTokens === "number" && modelInfo.maxTokens > 0 && (
+			<>
+				<span className="font-medium">{t("settings:modelInfo.maxOutput")}:</span>{" "}
+				{modelInfo.maxTokens?.toLocaleString()} tokens
+			</>
+		),
 		<ModelInfoSupportsItem
 			isSupported={modelInfo?.supportsImages ?? false}
 			supportsLabel={t("settings:modelInfo.supportsImages")}
@@ -41,18 +53,6 @@ export const ModelInfoView = ({
 			supportsLabel={t("settings:modelInfo.supportsPromptCache")}
 			doesNotSupportLabel={t("settings:modelInfo.noPromptCache")}
 		/>,
-		typeof modelInfo?.contextWindow === "number" && modelInfo.contextWindow > 0 && (
-			<>
-				<span className="font-medium">{t("settings:modelInfo.contextWindow")}</span>{" "}
-				{modelInfo.contextWindow?.toLocaleString()} tokens
-			</>
-		),
-		typeof modelInfo?.maxTokens === "number" && modelInfo.maxTokens > 0 && (
-			<>
-				<span className="font-medium">{t("settings:modelInfo.maxOutput")}:</span>{" "}
-				{modelInfo.maxTokens?.toLocaleString()} tokens
-			</>
-		),
 		modelInfo?.inputPrice !== undefined && modelInfo.inputPrice > 0 && (
 			<>
 				<span className="font-medium">{t("settings:modelInfo.inputPrice")}:</span>{" "}
@@ -119,11 +119,7 @@ const ModelInfoSupportsItem = ({
 	supportsLabel: string
 	doesNotSupportLabel: string
 }) => (
-	<div
-		className={cn(
-			"flex items-center gap-1 font-medium",
-			isSupported ? "text-vscode-charts-green" : "text-vscode-errorForeground",
-		)}>
+	<div className="flex items-center gap-1 font-medium">
 		<span className={cn("codicon", isSupported ? "codicon-check" : "codicon-x")} />
 		{isSupported ? supportsLabel : doesNotSupportLabel}
 	</div>

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

@@ -18,6 +18,7 @@ import {
 	doubaoModels,
 	internationalZAiModels,
 	fireworksModels,
+	rooModels,
 } from "@roo-code/types"
 
 export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, ModelInfo>>> = {
@@ -38,6 +39,7 @@ export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, Mod
 	sambanova: sambaNovaModels,
 	zai: internationalZAiModels,
 	fireworks: fireworksModels,
+	roo: rooModels,
 }
 
 export const PROVIDERS = [
@@ -70,4 +72,5 @@ export const PROVIDERS = [
 	{ value: "zai", label: "Z AI" },
 	{ value: "fireworks", label: "Fireworks AI" },
 	{ value: "io-intelligence", label: "IO Intelligence" },
+	{ value: "roo", label: "Roo Code Cloud" },
 ].sort((a, b) => a.label.localeCompare(b.label))

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

@@ -48,6 +48,8 @@ import {
 	fireworksDefaultModelId,
 	ioIntelligenceDefaultModelId,
 	ioIntelligenceModels,
+	rooDefaultModelId,
+	rooModels,
 	BEDROCK_CLAUDE_SONNET_4_MODEL_ID,
 } from "@roo-code/types"
 
@@ -296,6 +298,11 @@ function getSelectedModel({
 				routerModels["io-intelligence"]?.[id] ?? ioIntelligenceModels[id as keyof typeof ioIntelligenceModels]
 			return { id, info }
 		}
+		case "roo": {
+			const id = apiConfiguration.apiModelId ?? rooDefaultModelId
+			const info = rooModels[id as keyof typeof rooModels]
+			return { id, info }
+		}
 		// case "anthropic":
 		// case "human-relay":
 		// case "fake-ai":

+ 2 - 2
webview-ui/src/i18n/locales/ca/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "Versió {{version}} - Feu clic per veure les notes de llançament"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud arribarà aviat!",
+		"title": "Roo Code Cloud està evolucionant!",
 		"description": "Executa agents remots al núvol, accedeix a les teves tasques des de qualsevol lloc, col·labora amb altres i molt més.",
-		"joinWaitlist": "Uneix-te a la llista d'espera per obtenir accés anticipat."
+		"joinWaitlist": "Registra't per rebre les últimes actualitzacions."
 	},
 	"editMessage": {
 		"placeholder": "Edita el teu missatge..."

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

@@ -377,6 +377,10 @@
 			"description": "No es requereix clau API, però l'usuari necessita ajuda per copiar i enganxar informació al xat d'IA web.",
 			"instructions": "Durant l'ús, apareixerà un diàleg i el missatge actual es copiarà automàticament al porta-retalls. Necessiteu enganxar-lo a les versions web d'IA (com ChatGPT o Claude), després copiar la resposta de l'IA de nou al diàleg i fer clic al botó de confirmació."
 		},
+		"roo": {
+			"authenticatedMessage": "Autenticat de forma segura a través del teu compte de Roo Code Cloud.",
+			"connectButton": "Connecta amb Roo Code Cloud"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "Encaminament de Proveïdors d'OpenRouter",

+ 2 - 2
webview-ui/src/i18n/locales/de/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "Version {{version}} - Klicken Sie, um die Versionshinweise anzuzeigen"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud kommt bald!",
+		"title": "Roo Code Cloud entwickelt sich weiter!",
 		"description": "Führe Remote-Agenten in der Cloud aus, greife von überall auf deine Aufgaben zu, arbeite mit anderen zusammen und vieles mehr.",
-		"joinWaitlist": "Tritt der Warteliste bei, um frühen Zugang zu erhalten."
+		"joinWaitlist": "Melde dich an, um die neuesten Updates zu erhalten."
 	},
 	"command": {
 		"triggerDescription": "Starte den {{name}} Befehl"

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

@@ -377,6 +377,10 @@
 			"description": "Es ist kein API-Schlüssel erforderlich, aber der Benutzer muss beim Kopieren und Einfügen der Informationen in den Web-Chat-KI helfen.",
 			"instructions": "Während der Verwendung wird ein Dialogfeld angezeigt und die aktuelle Nachricht wird automatisch in die Zwischenablage kopiert. Du musst diese in Web-Versionen von KI (wie ChatGPT oder Claude) einfügen, dann die Antwort der KI zurück in das Dialogfeld kopieren und auf die Bestätigungsschaltfläche klicken."
 		},
+		"roo": {
+			"authenticatedMessage": "Sicher authentifiziert über dein Roo Code Cloud-Konto.",
+			"connectButton": "Mit Roo Code Cloud verbinden"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "OpenRouter Anbieter-Routing",

+ 2 - 2
webview-ui/src/i18n/locales/en/chat.json

@@ -352,9 +352,9 @@
 		"ariaLabel": "Version {{version}} - Click to view release notes"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud is coming soon!",
+		"title": "Roo Code Cloud is evolving!",
 		"description": "Run Roomote agents in the cloud, access your tasks from anywhere, collaborate with others, and more.",
-		"joinWaitlist": "Join the waitlist to get early access."
+		"joinWaitlist": "Sign up to get the latest updates."
 	},
 	"command": {
 		"triggerDescription": "Trigger the {{name}} command"

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

@@ -376,6 +376,10 @@
 			"description": "No API key is required, but the user needs to help copy and paste the information to the web chat AI.",
 			"instructions": "During use, a dialog box will pop up and the current message will be copied to the clipboard automatically. You need to paste these to web versions of AI (such as ChatGPT or Claude), then copy the AI's reply back to the dialog box and click the confirm button."
 		},
+		"roo": {
+			"authenticatedMessage": "Securely authenticated through your Roo Code Cloud account.",
+			"connectButton": "Connect to Roo Code Cloud"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "OpenRouter Provider Routing",

+ 2 - 2
webview-ui/src/i18n/locales/es/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "Versión {{version}} - Haz clic para ver las notas de la versión"
 	},
 	"rooCloudCTA": {
-		"title": "¡Roo Code Cloud llegará pronto!",
+		"title": "¡Roo Code Cloud está evolucionando!",
 		"description": "Ejecuta agentes remotos en la nube, accede a tus tareas desde cualquier lugar, colabora con otros y mucho más.",
-		"joinWaitlist": "Únete a la lista de espera para obtener acceso anticipado."
+		"joinWaitlist": "Regístrate para recibir las últimas actualizaciones."
 	},
 	"editMessage": {
 		"placeholder": "Edita tu mensaje..."

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

@@ -377,6 +377,10 @@
 			"description": "No se requiere clave API, pero el usuario necesita ayudar a copiar y pegar la información en el chat web de IA.",
 			"instructions": "Durante el uso, aparecerá un cuadro de diálogo y el mensaje actual se copiará automáticamente al portapapeles. Debe pegarlo en las versiones web de IA (como ChatGPT o Claude), luego copiar la respuesta de la IA de vuelta al cuadro de diálogo y hacer clic en el botón de confirmar."
 		},
+		"roo": {
+			"authenticatedMessage": "Autenticado de forma segura a través de tu cuenta de Roo Code Cloud.",
+			"connectButton": "Conectar a Roo Code Cloud"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "Enrutamiento de Proveedores de OpenRouter",

+ 2 - 2
webview-ui/src/i18n/locales/fr/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "Version {{version}} - Cliquez pour voir les notes de version"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud arrive bientôt !",
+		"title": "Roo Code Cloud évolue !",
 		"description": "Exécutez des agents distants dans le cloud, accédez à vos tâches de n'importe où, collaborez avec d'autres et bien plus encore.",
-		"joinWaitlist": "Rejoignez la liste d'attente pour obtenir un accès anticipé."
+		"joinWaitlist": "Inscrivez-vous pour recevoir les dernières mises à jour."
 	},
 	"editMessage": {
 		"placeholder": "Modifiez votre message..."

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

@@ -377,6 +377,10 @@
 			"description": "Aucune clé API n'est requise, mais l'utilisateur doit aider à copier et coller les informations dans le chat web de l'IA.",
 			"instructions": "Pendant l'utilisation, une boîte de dialogue apparaîtra et le message actuel sera automatiquement copié dans le presse-papiers. Vous devez le coller dans les versions web de l'IA (comme ChatGPT ou Claude), puis copier la réponse de l'IA dans la boîte de dialogue et cliquer sur le bouton de confirmation."
 		},
+		"roo": {
+			"authenticatedMessage": "Authentifié de manière sécurisée via ton compte Roo Code Cloud.",
+			"connectButton": "Se connecter à Roo Code Cloud"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "Routage des fournisseurs OpenRouter",

+ 2 - 2
webview-ui/src/i18n/locales/hi/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "संस्करण {{version}} - रिलीज़ नोट्स देखने के लिए क्लिक करें"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud जल्द आ रहा है!",
+		"title": "Roo Code Cloud विकसित हो रहा है!",
 		"description": "क्लाउड में रिमोट एजेंट चलाएं, कहीं से भी अपने कार्यों तक पहुंचें, दूसरों के साथ सहयोग करें, और बहुत कुछ।",
-		"joinWaitlist": "जल्दी पहुंच पाने के लिए प्रतीक्षा सूची में शामिल हों।"
+		"joinWaitlist": "नवीनतम अपडेट प्राप्त करने के लिए साइन अप करें।"
 	},
 	"editMessage": {
 		"placeholder": "अपना संदेश संपादित करें..."

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

@@ -377,6 +377,10 @@
 			"description": "कोई API कुंजी आवश्यक नहीं है, लेकिन उपयोगकर्ता को वेब चैट AI में जानकारी कॉपी और पेस्ट करने में मदद करनी होगी।",
 			"instructions": "उपयोग के दौरान, एक डायलॉग बॉक्स पॉप अप होगा और वर्तमान संदेश स्वचालित रूप से क्लिपबोर्ड पर कॉपी हो जाएगा। आपको इन्हें AI के वेब संस्करणों (जैसे ChatGPT या Claude) में पेस्ट करना होगा, फिर AI की प्रतिक्रिया को डायलॉग बॉक्स में वापस कॉपी करें और पुष्टि बटन पर क्लिक करें।"
 		},
+		"roo": {
+			"authenticatedMessage": "आपके Roo Code Cloud खाते के माध्यम से सुरक्षित रूप से प्रमाणित।",
+			"connectButton": "Roo Code Cloud से कनेक्ट करें"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "OpenRouter प्रदाता रूटिंग",

+ 2 - 2
webview-ui/src/i18n/locales/id/chat.json

@@ -355,9 +355,9 @@
 		"ariaLabel": "Versi {{version}} - Klik untuk melihat catatan rilis"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud segera hadir!",
+		"title": "Roo Code Cloud sedang berkembang!",
 		"description": "Jalankan agen jarak jauh di cloud, akses tugas Anda dari mana saja, berkolaborasi dengan orang lain, dan banyak lagi.",
-		"joinWaitlist": "Bergabunglah dengan daftar tunggu untuk mendapatkan akses awal."
+		"joinWaitlist": "Daftar untuk mendapatkan pembaruan terbaru."
 	},
 	"editMessage": {
 		"placeholder": "Edit pesan Anda..."

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

@@ -381,6 +381,10 @@
 			"description": "Tidak diperlukan API key, tetapi pengguna perlu membantu menyalin dan menempel informasi ke web chat AI.",
 			"instructions": "Selama penggunaan, kotak dialog akan muncul dan pesan saat ini akan disalin ke clipboard secara otomatis. Kamu perlu menempel ini ke versi web AI (seperti ChatGPT atau Claude), lalu salin balasan AI kembali ke kotak dialog dan klik tombol konfirmasi."
 		},
+		"roo": {
+			"authenticatedMessage": "Terautentikasi dengan aman melalui akun Roo Code Cloud Anda.",
+			"connectButton": "Hubungkan ke Roo Code Cloud"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "OpenRouter Provider Routing",

+ 2 - 2
webview-ui/src/i18n/locales/it/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "Versione {{version}} - Clicca per visualizzare le note di rilascio"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud arriva presto!",
+		"title": "Roo Code Cloud si sta evolvendo!",
 		"description": "Esegui agenti remoti nel cloud, accedi alle tue attività da qualsiasi luogo, collabora con altri e molto altro.",
-		"joinWaitlist": "Unisciti alla lista d'attesa per ottenere l'accesso anticipato."
+		"joinWaitlist": "Registrati per ricevere gli ultimi aggiornamenti."
 	},
 	"editMessage": {
 		"placeholder": "Modifica il tuo messaggio..."

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

@@ -377,6 +377,10 @@
 			"description": "Non è richiesta alcuna chiave API, ma l'utente dovrà aiutare a copiare e incollare le informazioni nella chat web AI.",
 			"instructions": "Durante l'uso, apparirà una finestra di dialogo e il messaggio corrente verrà automaticamente copiato negli appunti. Dovrai incollarlo nelle versioni web dell'AI (come ChatGPT o Claude), quindi copiare la risposta dell'AI nella finestra di dialogo e fare clic sul pulsante di conferma."
 		},
+		"roo": {
+			"authenticatedMessage": "Autenticato in modo sicuro tramite il tuo account Roo Code Cloud.",
+			"connectButton": "Connetti a Roo Code Cloud"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "Routing dei fornitori OpenRouter",

+ 2 - 2
webview-ui/src/i18n/locales/ja/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "バージョン {{version}} - クリックしてリリースノートを表示"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud が間もなく登場!",
+		"title": "Roo Code Cloud が進化中!",
 		"description": "クラウドでリモートエージェントを実行し、どこからでもタスクにアクセスし、他の人と協力し、その他多くの機能を利用できます。",
-		"joinWaitlist": "早期アクセスを取得するためにウェイトリストに参加してください。"
+		"joinWaitlist": "最新のアップデートを受け取るためにサインアップしてください。"
 	},
 	"editMessage": {
 		"placeholder": "メッセージを編集..."

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

@@ -377,6 +377,10 @@
 			"description": "APIキーは不要ですが、ユーザーはウェブチャットAIに情報をコピー&ペーストする必要があります。",
 			"instructions": "使用中にダイアログボックスが表示され、現在のメッセージが自動的にクリップボードにコピーされます。これらをウェブ版のAI(ChatGPTやClaudeなど)に貼り付け、AIの返答をダイアログボックスにコピーして確認ボタンをクリックする必要があります。"
 		},
+		"roo": {
+			"authenticatedMessage": "Roo Code Cloudアカウントを通じて安全に認証されています。",
+			"connectButton": "Roo Code Cloudに接続"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "OpenRouterプロバイダールーティング",

+ 2 - 2
webview-ui/src/i18n/locales/ko/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "버전 {{version}} - 릴리스 노트를 보려면 클릭하세요"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud가 곧 출시됩니다!",
+		"title": "Roo Code Cloud가 진화하고 있습니다!",
 		"description": "클라우드에서 원격 에이전트를 실행하고, 어디서나 작업에 액세스하고, 다른 사람들과 협업하는 등 다양한 기능을 이용하세요.",
-		"joinWaitlist": "얼리 액세스를 받으려면 대기 목록에 가입하세요."
+		"joinWaitlist": "최신 업데이트를 받으려면 가입하세요."
 	},
 	"editMessage": {
 		"placeholder": "메시지 편집..."

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

@@ -377,6 +377,10 @@
 			"description": "API 키가 필요하지 않지만, 사용자가 웹 채팅 AI에 정보를 복사하여 붙여넣어야 합니다.",
 			"instructions": "사용 중에 대화 상자가 나타나고 현재 메시지가 자동으로 클립보드에 복사됩니다. 이를 웹 버전 AI(예: ChatGPT 또는 Claude)에 붙여넣은 다음, AI의 응답을 대화 상자에 복사하고 확인 버튼을 클릭해야 합니다."
 		},
+		"roo": {
+			"authenticatedMessage": "Roo Code Cloud 계정을 통해 안전하게 인증되었습니다.",
+			"connectButton": "Roo Code Cloud에 연결"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "OpenRouter 제공자 라우팅",

+ 2 - 2
webview-ui/src/i18n/locales/nl/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "Versie {{version}} - Klik om release notes te bekijken"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud komt binnenkort!",
+		"title": "Roo Code Cloud evolueert!",
 		"description": "Voer externe agenten uit in de cloud, krijg overal toegang tot je taken, werk samen met anderen en nog veel meer.",
-		"joinWaitlist": "Sluit je aan bij de wachtlijst voor vroege toegang."
+		"joinWaitlist": "Meld je aan om de laatste updates te ontvangen."
 	},
 	"editMessage": {
 		"placeholder": "Bewerk je bericht..."

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

@@ -377,6 +377,10 @@
 			"description": "Geen API-sleutel vereist, maar de gebruiker moet helpen met kopiëren en plakken naar de webchat-AI.",
 			"instructions": "Tijdens gebruik verschijnt een dialoogvenster en wordt het huidige bericht automatisch naar het klembord gekopieerd. Je moet deze plakken in webversies van AI (zoals ChatGPT of Claude), vervolgens het antwoord van de AI terugkopiëren naar het dialoogvenster en op bevestigen klikken."
 		},
+		"roo": {
+			"authenticatedMessage": "Veilig geauthenticeerd via je Roo Code Cloud-account.",
+			"connectButton": "Verbinden met Roo Code Cloud"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "OpenRouter-providerroutering",

+ 2 - 2
webview-ui/src/i18n/locales/pl/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "Wersja {{version}} - Kliknij, aby wyświetlić informacje o wydaniu"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud już wkrótce!",
+		"title": "Roo Code Cloud się rozwija!",
 		"description": "Uruchamiaj zdalne agenty w chmurze, uzyskuj dostęp do swoich zadań z dowolnego miejsca, współpracuj z innymi i wiele więcej.",
-		"joinWaitlist": "Dołącz do listy oczekujących, aby uzyskać wczesny dostęp."
+		"joinWaitlist": "Zarejestruj się, aby otrzymywać najnowsze aktualizacje."
 	},
 	"editMessage": {
 		"placeholder": "Edytuj swoją wiadomość..."

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

@@ -377,6 +377,10 @@
 			"description": "Nie jest wymagany klucz API, ale użytkownik będzie musiał pomóc w kopiowaniu i wklejaniu informacji do czatu internetowego AI.",
 			"instructions": "Podczas użytkowania pojawi się okno dialogowe, a bieżąca wiadomość zostanie automatycznie skopiowana do schowka. Będziesz musiał wkleić ją do internetowych wersji AI (takich jak ChatGPT lub Claude), a następnie skopiować odpowiedź AI z powrotem do okna dialogowego i kliknąć przycisk potwierdzenia."
 		},
+		"roo": {
+			"authenticatedMessage": "Bezpiecznie uwierzytelniony przez twoje konto Roo Code Cloud.",
+			"connectButton": "Połącz z Roo Code Cloud"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "Routing dostawców OpenRouter",

+ 2 - 2
webview-ui/src/i18n/locales/pt-BR/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "Versão {{version}} - Clique para ver as notas de lançamento"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud chegará em breve!",
+		"title": "Roo Code Cloud está evoluindo!",
 		"description": "Execute agentes remotos na nuvem, acesse suas tarefas de qualquer lugar, colabore com outros e muito mais.",
-		"joinWaitlist": "Junte-se à lista de espera para obter acesso antecipado."
+		"joinWaitlist": "Cadastre-se para receber as últimas atualizações."
 	},
 	"editMessage": {
 		"placeholder": "Edite sua mensagem..."

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

@@ -377,6 +377,10 @@
 			"description": "Não é necessária chave de API, mas o usuário precisa ajudar a copiar e colar as informações para a IA do chat web.",
 			"instructions": "Durante o uso, uma caixa de diálogo será exibida e a mensagem atual será copiada para a área de transferência automaticamente. Você precisa colar isso nas versões web de IA (como ChatGPT ou Claude), depois copiar a resposta da IA de volta para a caixa de diálogo e clicar no botão confirmar."
 		},
+		"roo": {
+			"authenticatedMessage": "Autenticado com segurança através da sua conta Roo Code Cloud.",
+			"connectButton": "Conectar ao Roo Code Cloud"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "Roteamento de Provedores OpenRouter",

+ 2 - 2
webview-ui/src/i18n/locales/ru/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "Версия {{version}} - Нажмите, чтобы просмотреть примечания к выпуску"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud скоро появится!",
+		"title": "Roo Code Cloud развивается!",
 		"description": "Запускайте удаленные агенты в облаке, получайте доступ к своим задачам из любого места, сотрудничайте с другими и многое другое.",
-		"joinWaitlist": "Присоединитесь к списку ожидания для получения раннего доступа."
+		"joinWaitlist": "Зарегистрируйтесь, чтобы получать последние обновления."
 	},
 	"editMessage": {
 		"placeholder": "Редактировать сообщение..."

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

@@ -377,6 +377,10 @@
 			"description": "API-ключ не требуется, но пользователю нужно вручную копировать и вставлять информацию в веб-чат ИИ.",
 			"instructions": "Во время использования появится диалоговое окно, и текущее сообщение будет скопировано в буфер обмена автоматически. Вам нужно вставить его в веб-версию ИИ (например, ChatGPT или Claude), затем скопировать ответ ИИ обратно в диалоговое окно и нажать кнопку подтверждения."
 		},
+		"roo": {
+			"authenticatedMessage": "Безопасно аутентифицирован через твой аккаунт Roo Code Cloud.",
+			"connectButton": "Подключиться к Roo Code Cloud"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "Маршрутизация провайдера OpenRouter",

+ 2 - 2
webview-ui/src/i18n/locales/tr/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "Sürüm {{version}} - Sürüm notlarını görüntülemek için tıklayın"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud yakında geliyor!",
+		"title": "Roo Code Cloud geliyor!",
 		"description": "Bulutta uzak ajanlar çalıştırın, görevlerinize her yerden erişin, başkalarıyla işbirliği yapın ve daha fazlası.",
-		"joinWaitlist": "Erken erişim için bekleme listesine katılın."
+		"joinWaitlist": "En son güncellemeleri almak için kaydolun."
 	},
 	"editMessage": {
 		"placeholder": "Mesajını düzenle..."

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

@@ -377,6 +377,10 @@
 			"description": "API anahtarı gerekmez, ancak kullanıcının bilgileri web sohbet yapay zekasına kopyalayıp yapıştırması gerekir.",
 			"instructions": "Kullanım sırasında bir iletişim kutusu açılacak ve mevcut mesaj otomatik olarak panoya kopyalanacaktır. Bunları web yapay zekalarına (ChatGPT veya Claude gibi) yapıştırmanız, ardından yapay zekanın yanıtını iletişim kutusuna kopyalayıp onay düğmesine tıklamanız gerekir."
 		},
+		"roo": {
+			"authenticatedMessage": "Roo Code Cloud hesabın üzerinden güvenli bir şekilde kimlik doğrulandı.",
+			"connectButton": "Roo Code Cloud'a Bağlan"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "OpenRouter Sağlayıcı Yönlendirmesi",

+ 2 - 2
webview-ui/src/i18n/locales/vi/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "Phiên bản {{version}} - Nhấp để xem ghi chú phát hành"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud sắp ra mắt!",
+		"title": "Roo Code Cloud đang phát triển!",
 		"description": "Chạy các agent từ xa trên cloud, truy cập các tác vụ của bạn từ mọi nơi, cộng tác với người khác và nhiều hơn nữa.",
-		"joinWaitlist": "Tham gia danh sách chờ để được truy cập sớm."
+		"joinWaitlist": "Đăng ký để nhận các cập nhật mới nhất."
 	},
 	"editMessage": {
 		"placeholder": "Chỉnh sửa tin nhắn của bạn..."

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

@@ -377,6 +377,10 @@
 			"description": "Không cần khóa API, nhưng người dùng cần giúp sao chép và dán thông tin vào AI trò chuyện web.",
 			"instructions": "Trong quá trình sử dụng, một hộp thoại sẽ xuất hiện và tin nhắn hiện tại sẽ được tự động sao chép vào clipboard. Bạn cần dán chúng vào các phiên bản web của AI (như ChatGPT hoặc Claude), sau đó sao chép phản hồi của AI trở lại hộp thoại và nhấp vào nút xác nhận."
 		},
+		"roo": {
+			"authenticatedMessage": "Đã xác thực an toàn thông qua tài khoản Roo Code Cloud của bạn.",
+			"connectButton": "Kết nối với Roo Code Cloud"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "Định tuyến nhà cung cấp OpenRouter",

+ 2 - 2
webview-ui/src/i18n/locales/zh-CN/chat.json

@@ -349,9 +349,9 @@
 		"ariaLabel": "版本 {{version}} - 点击查看发布说明"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud 即将推出!",
+		"title": "Roo Code Cloud 正在进化!",
 		"description": "在云端运行远程代理,随时随地访问任务,与他人协作等更多功能。",
-		"joinWaitlist": "加入等待列表获取早期访问权限。"
+		"joinWaitlist": "注册获取最新更新。"
 	},
 	"command": {
 		"triggerDescription": "触发 {{name}} 命令"

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

@@ -377,6 +377,10 @@
 			"description": "不需要 API 密钥,但用户需要帮助将信息复制并粘贴到网页聊天 AI。",
 			"instructions": "使用期间,将弹出对话框并自动将当前消息复制到剪贴板。您需要将这些内容粘贴到 AI 的网页版本(如 ChatGPT 或 Claude),然后将 AI 的回复复制回对话框并点击确认按钮。"
 		},
+		"roo": {
+			"authenticatedMessage": "已通过 Roo Code Cloud 账户安全认证。",
+			"connectButton": "连接到 Roo Code Cloud"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "OpenRouter 提供商路由",

+ 2 - 2
webview-ui/src/i18n/locales/zh-TW/chat.json

@@ -352,9 +352,9 @@
 		"ariaLabel": "版本 {{version}} - 點選查看發布說明"
 	},
 	"rooCloudCTA": {
-		"title": "Roo Code Cloud 即將推出!",
+		"title": "Roo Code Cloud 正在進化!",
 		"description": "在雲端執行 Roomote 遠端代理、隨時隨地存取您的工作、與他人協作等等。",
-		"joinWaitlist": "加入等候名單以獲得早期存取權限。"
+		"joinWaitlist": "註冊以獲得最新更新。"
 	},
 	"command": {
 		"triggerDescription": "觸發 {{name}} 命令"

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

@@ -377,6 +377,10 @@
 			"description": "不需要 API 金鑰,但使用者需要協助將資訊複製並貼上到網頁聊天 AI。",
 			"instructions": "使用期間會彈出對話框,並自動將目前訊息複製到剪貼簿。您需要將這些內容貼上到網頁版 AI(如 ChatGPT 或 Claude),然後將 AI 的回覆複製回對話框並點選確認按鈕。"
 		},
+		"roo": {
+			"authenticatedMessage": "已透過 Roo Code Cloud 帳戶安全認證。",
+			"connectButton": "連接到 Roo Code Cloud"
+		},
 		"openRouter": {
 			"providerRouting": {
 				"title": "OpenRouter 供應商路由",