diff --git a/apps/web-roo-code/src/app/privacy/page.tsx b/apps/web-roo-code/src/app/privacy/page.tsx index bb132651f7..b0efdbc444 100644 --- a/apps/web-roo-code/src/app/privacy/page.tsx +++ b/apps/web-roo-code/src/app/privacy/page.tsx @@ -86,8 +86,8 @@ export default function Privacy() { Your source code does not transit Roo Code servers unless you explicitly choose Roo Code as a model provider (proxy mode). {" "} - When Roo Code Cloud is your model provider, your code briefly transits Roo Code servers only to - forward it to the upstream model, is not stored, and is deleted immediately after + When Roo Code Cloud is your model provider, your code briefly transits Roo Code servers only + to forward it to the upstream model, is not stored, and is deleted immediately after forwarding. Otherwise, your code is sent directly—via client‑to‑provider TLS—to the model you select. Roo Code never stores, inspects, or trains on your code. diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index d5f8261174..fca3a1e32b 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -199,6 +199,9 @@ export const SECRET_STATE_KEYS = [ "codebaseIndexMistralApiKey", "codebaseIndexVercelAiGatewayApiKey", "huggingFaceApiKey", + "watsonxApiKey", + "codebaseIndexWatsonxApiKey", + "codebaseIndexWatsonxProjectId", "sambaNovaApiKey", "zaiApiKey", "fireworksApiKey", diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index c7fd0fda53..8fa3328c22 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -60,6 +60,7 @@ export const providerNames = [ "chutes", "litellm", "huggingface", + "watsonx", "cerebras", "sambanova", "zai", @@ -388,6 +389,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv huggingFaceSchema.merge(z.object({ apiProvider: z.literal("huggingface") })), chutesSchema.merge(z.object({ apiProvider: z.literal("chutes") })), litellmSchema.merge(z.object({ apiProvider: z.literal("litellm") })), + watsonxSchema.merge(z.object({ apiProvider: z.literal("watsonx") })), cerebrasSchema.merge(z.object({ apiProvider: z.literal("cerebras") })), sambaNovaSchema.merge(z.object({ apiProvider: z.literal("sambanova") })), zaiSchema.merge(z.object({ apiProvider: z.literal("zai") })), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 58a1a9d27c..415171508c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -703,7 +703,7 @@ importers: version: 25.2.1(typescript@5.8.3) ibm-cloud-sdk-core: specifier: ^5.4.2 - version: 5.4.2 + version: 5.4.3 ignore: specifier: ^7.0.3 version: 7.0.4 @@ -4511,6 +4511,9 @@ packages: axios@1.12.0: resolution: {integrity: sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg==} + axios@1.12.2: + resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==} + azure-devops-node-api@12.5.0: resolution: {integrity: sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==} @@ -6037,15 +6040,6 @@ packages: debug: optional: true - follow-redirects@1.15.9: - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -6462,6 +6456,10 @@ packages: typescript: optional: true + ibm-cloud-sdk-core@5.4.3: + resolution: {integrity: sha512-D0lvClcoCp/HXyaFlCbOT4aTYgGyeIb4ncxZpxRuiuw7Eo79C6c49W53+8WJRD9nxzT5vrIdaky3NBcTdBtaEg==} + engines: {node: '>=18'} + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -8651,6 +8649,9 @@ packages: require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resize-observer-polyfill@1.5.1: resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} @@ -11406,7 +11407,7 @@ snapshots: dependencies: '@types/node': 18.19.100 extend: 3.0.2 - ibm-cloud-sdk-core: 5.4.2 + ibm-cloud-sdk-core: 5.4.3 transitivePeerDependencies: - supports-color @@ -13934,7 +13935,7 @@ snapshots: sirv: 3.0.1 tinyglobby: 0.2.14 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@20.17.57)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.2.1)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0) '@vitest/utils@3.2.4': dependencies: @@ -14257,7 +14258,15 @@ snapshots: axios@1.12.0: dependencies: - follow-redirects: 1.15.11 + follow-redirects: 1.15.11(debug@4.4.1) + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + axios@1.12.2(debug@4.4.1): + dependencies: + follow-redirects: 1.15.11(debug@4.4.1) form-data: 4.0.4 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -15938,9 +15947,9 @@ snapshots: flatted@3.3.3: {} - follow-redirects@1.15.11: {} - - follow-redirects@1.15.9: {} + follow-redirects@1.15.11(debug@4.4.1): + optionalDependencies: + debug: 4.4.1(supports-color@8.1.1) for-each@0.3.5: dependencies: @@ -16463,6 +16472,26 @@ snapshots: optionalDependencies: typescript: 5.8.3 + ibm-cloud-sdk-core@5.4.3: + dependencies: + '@types/debug': 4.1.12 + '@types/node': 18.19.100 + '@types/tough-cookie': 4.0.5 + axios: 1.12.2(debug@4.4.1) + camelcase: 6.3.0 + debug: 4.4.1(supports-color@8.1.1) + dotenv: 16.5.0 + extend: 3.0.2 + file-type: 16.5.4 + form-data: 4.0.4 + isstream: 0.1.2 + jsonwebtoken: 9.0.2 + mime-types: 2.1.35 + retry-axios: 2.6.0(axios@1.12.2(debug@4.4.1)) + tough-cookie: 4.1.4 + transitivePeerDependencies: + - supports-color + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -18183,7 +18212,7 @@ snapshots: dependencies: '@vscode/vsce': 3.3.2 commander: 6.2.1 - follow-redirects: 1.15.9(debug@4.4.1) + follow-redirects: 1.15.11(debug@4.4.1) is-ci: 2.0.0 leven: 3.1.0 semver: 7.7.2 @@ -19047,6 +19076,8 @@ snapshots: require-main-filename@2.0.0: {} + requires-port@1.0.0: {} + resize-observer-polyfill@1.5.1: {} resolve-from@4.0.0: {} @@ -19072,9 +19103,9 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 - retry-axios@2.6.0(axios@1.11.0(debug@4.4.1)): + retry-axios@2.6.0(axios@1.12.2(debug@4.4.1)): dependencies: - axios: 1.11.0(debug@4.4.1) + axios: 1.12.2(debug@4.4.1) retry@0.12.0: {} diff --git a/src/api/providers/featherless.ts b/src/api/providers/featherless.ts index 56d7177de7..2a985e2a87 100644 --- a/src/api/providers/featherless.ts +++ b/src/api/providers/featherless.ts @@ -1,4 +1,9 @@ -import { DEEP_SEEK_DEFAULT_TEMPERATURE, type FeatherlessModelId, featherlessDefaultModelId, featherlessModels } from "@roo-code/types" +import { + DEEP_SEEK_DEFAULT_TEMPERATURE, + type FeatherlessModelId, + featherlessDefaultModelId, + featherlessModels, +} from "@roo-code/types" import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" diff --git a/src/api/providers/fetchers/modelCache.ts b/src/api/providers/fetchers/modelCache.ts index a98b446f99..0cb5c0410c 100644 --- a/src/api/providers/fetchers/modelCache.ts +++ b/src/api/providers/fetchers/modelCache.ts @@ -21,7 +21,6 @@ import { getLMStudioModels } from "./lmstudio" import { getIOIntelligenceModels } from "./io-intelligence" import { getDeepInfraModels } from "./deepinfra" import { getWatsonxModels } from "./watsonx" - const memoryCache = new NodeCache({ stdTTL: 5 * 60, checkperiod: 5 * 60 }) async function writeModels(router: RouterName, data: ModelRecord) { diff --git a/src/api/providers/index.ts b/src/api/providers/index.ts index cc4627837e..e1f118f3c1 100644 --- a/src/api/providers/index.ts +++ b/src/api/providers/index.ts @@ -28,6 +28,7 @@ export { UnboundHandler } from "./unbound" export { VertexHandler } from "./vertex" export { VsCodeLmHandler } from "./vscode-lm" export { XAIHandler } from "./xai" +export { WatsonxAIHandler } from "./watsonx" export { ZAiHandler } from "./zai" export { FireworksHandler } from "./fireworks" export { RooHandler } from "./roo" diff --git a/src/core/tools/codebaseSearchTool.ts b/src/core/tools/codebaseSearchTool.ts index 700d1b7c7c..263fa8755e 100644 --- a/src/core/tools/codebaseSearchTool.ts +++ b/src/core/tools/codebaseSearchTool.ts @@ -17,7 +17,7 @@ export async function codebaseSearchTool( removeClosingTag: RemoveClosingTag, ) { const toolName = "codebase_search" - const workspacePath = (cline.cwd && cline.cwd.trim() !== '') ? cline.cwd : getWorkspacePath() + const workspacePath = cline.cwd && cline.cwd.trim() !== "" ? cline.cwd : getWorkspacePath() if (!workspacePath) { // This case should ideally not happen if Cline is initialized correctly diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index aa831e6c1b..14b333d3e6 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -11,7 +11,7 @@ import { type ClineMessage, type TelemetrySetting, TelemetryEventName, - UserSettingsConfig + UserSettingsConfig, } from "@roo-code/types" import { CloudService } from "@roo-code/cloud" import { TelemetryService } from "@roo-code/telemetry" @@ -2545,7 +2545,6 @@ export const webviewMessageHandler = async ( settings.codebaseIndexVercelAiGatewayApiKey, ) } - if (settings.codebaseIndexWatsonxApiKey !== undefined) { await provider.contextProxy.storeSecret( "codebaseIndexWatsonxApiKey", @@ -2558,7 +2557,6 @@ export const webviewMessageHandler = async ( settings.codebaseIndexWatsonxProjectId, ) } - // Send success response first - settings are saved regardless of validation await provider.postMessageToWebview({ type: "codeIndexSettingsSaved", diff --git a/src/i18n/locales/en/embeddings.json b/src/i18n/locales/en/embeddings.json index b6b5fdebc8..3601007927 100644 --- a/src/i18n/locales/en/embeddings.json +++ b/src/i18n/locales/en/embeddings.json @@ -47,6 +47,7 @@ "openAiCompatibleConfigMissing": "OpenAI Compatible configuration missing for embedder creation", "geminiConfigMissing": "Gemini configuration missing for embedder creation", "mistralConfigMissing": "Mistral configuration missing for embedder creation", + "watsonxConfigMissing": "IBM watsonx configuration missing for embedder creation", "vercelAiGatewayConfigMissing": "Vercel AI Gateway configuration missing for embedder creation", "watsonxConfigMissing": "IBM watsonx configuration missing for embedder creation", "invalidEmbedderType": "Invalid embedder type configured: {{embedderProvider}}", diff --git a/src/services/code-index/config-manager.ts b/src/services/code-index/config-manager.ts index 24c8577d97..7ecb72d3ee 100644 --- a/src/services/code-index/config-manager.ts +++ b/src/services/code-index/config-manager.ts @@ -252,12 +252,15 @@ export class CodeIndexConfigManager { const qdrantUrl = this.qdrantUrl const isConfigured = !!(apiKey && qdrantUrl) return isConfigured - } else if (this.embedderProvider === "vercel-ai-gateway") { - const apiKey = this.vercelAiGatewayOptions?.apiKey } else if (this.embedderProvider === "watsonx") { const apiKey = this.watsonxOptions?.codebaseIndexWatsonxApiKey + const projectId = this.watsonxOptions?.codebaseIndexWatsonxProjectId const qdrantUrl = this.qdrantUrl - const isConfigured = !!(apiKey && qdrantUrl) + const isConfigured = !!(apiKey && projectId && qdrantUrl) + return isConfigured + } else if (this.embedderProvider === "vercel-ai-gateway") { + const apiKey = this.vercelAiGatewayOptions?.apiKey + const isConfigured = !!apiKey return isConfigured } return false // Should not happen if embedderProvider is always set correctly @@ -362,14 +365,13 @@ export class CodeIndexConfigManager { return true } - if (prevVercelAiGatewayApiKey !== currentVercelAiGatewayApiKey) { - return true - } - if (prevWatsonxApiKey !== currentWatsonxApiKey || prevWatsonxProjectId !== currentWatsonxProjectId) { return true } + if (prevVercelAiGatewayApiKey !== currentVercelAiGatewayApiKey) { + return true + } // Check for model dimension changes (generic for all providers) if (prevModelDimension !== currentModelDimension) { return true diff --git a/src/services/tree-sitter/queries/c-sharp.ts b/src/services/tree-sitter/queries/c-sharp.ts index 350c24fff6..46f9651b36 100644 --- a/src/services/tree-sitter/queries/c-sharp.ts +++ b/src/services/tree-sitter/queries/c-sharp.ts @@ -63,5 +63,3 @@ export default ` ; LINQ expressions (query_expression) @definition.linq_expression ` - - \ No newline at end of file diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index eb7f4864e6..27fecff60f 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -14,6 +14,7 @@ import type { CloudUserInfo, ShareVisibility, QueuedMessage, + ModelInfo, } from "@roo-code/types" import { GitCommit } from "../utils/git" diff --git a/src/shared/api.ts b/src/shared/api.ts index 4d02e6937c..d302121a6b 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -156,5 +156,4 @@ export type GetModelsOptions = | { provider: "deepinfra"; apiKey?: string; baseUrl?: string } | { provider: "io-intelligence"; apiKey: string } | { provider: "vercel-ai-gateway" } - | { provider: "watsonx"; apiKey: string } | { provider: "watsonx"; apiKey: string; baseUrl: string } diff --git a/webview-ui/src/components/chat/CodeIndexPopover.tsx b/webview-ui/src/components/chat/CodeIndexPopover.tsx index 436d85af02..8933b2b089 100644 --- a/webview-ui/src/components/chat/CodeIndexPopover.tsx +++ b/webview-ui/src/components/chat/CodeIndexPopover.tsx @@ -408,6 +408,10 @@ export const CodeIndexPopover: React.FC = ({ if (!prev.codebaseIndexMistralApiKey || prev.codebaseIndexMistralApiKey === SECRET_PLACEHOLDER) { updated.codebaseIndexMistralApiKey = secretStatus.hasMistralApiKey ? SECRET_PLACEHOLDER : "" } + if (!prev.codebaseIndexWatsonxApiKey || prev.codebaseIndexWatsonxApiKey === SECRET_PLACEHOLDER) { + updated.codebaseIndexWatsonxApiKey = secretStatus.hasWatsonxApiKey ? SECRET_PLACEHOLDER : "" + } + if ( !prev.codebaseIndexVercelAiGatewayApiKey || prev.codebaseIndexVercelAiGatewayApiKey === SECRET_PLACEHOLDER @@ -427,6 +431,15 @@ export const CodeIndexPopover: React.FC = ({ : "" } + if ( + !prev.codebaseIndexWatsonxProjectId || + prev.codebaseIndexWatsonxProjectId === SECRET_PLACEHOLDER + ) { + updated.codebaseIndexWatsonxProjectId = secretStatus.hasWatsonxProjectId + ? SECRET_PLACEHOLDER + : "" + } + return updated } diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 578c3377e3..18d296de1b 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -28,6 +28,7 @@ import { chutesDefaultModelId, bedrockDefaultModelId, vertexDefaultModelId, + watsonxAiDefaultModelId, sambaNovaDefaultModelId, internationalZAiDefaultModelId, mainlandZAiDefaultModelId, @@ -623,6 +624,15 @@ const ApiOptions = ({ /> )} + {selectedProvider === "watsonx" && ( + + )} + {selectedProvider === "sambanova" && ( )} diff --git a/webview-ui/src/components/settings/constants.ts b/webview-ui/src/components/settings/constants.ts index f8e7716a0c..81c1f97639 100644 --- a/webview-ui/src/components/settings/constants.ts +++ b/webview-ui/src/components/settings/constants.ts @@ -15,6 +15,7 @@ import { xaiModels, groqModels, chutesModels, + watsonxAiModels, sambaNovaModels, doubaoModels, internationalZAiModels, @@ -40,6 +41,7 @@ export const MODELS_BY_PROVIDER: Partial