Browse Source

Move the native tool call toggle to experimental settings (#9297)

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Co-authored-by: daniel-lxs <[email protected]>
Matt Rubens 2 months ago
parent
commit
3e0bd0ea55
52 changed files with 242 additions and 147 deletions
  1. 0 12
      packages/build/src/__tests__/index.test.ts
  2. 2 0
      packages/types/src/experiment.ts
  3. 13 11
      src/core/assistant-message/presentAssistantMessage.ts
  4. 3 4
      src/core/prompts/responses.ts
  5. 31 5
      src/core/task/Task.ts
  6. 4 3
      src/core/tools/MultiApplyDiffTool.ts
  7. 7 1
      src/core/tools/ReadFileTool.ts
  8. 1 6
      src/core/tools/__tests__/applyDiffTool.experiment.spec.ts
  9. 1 1
      src/core/webview/generateSystemPrompt.ts
  10. 0 9
      src/package.json
  11. 1 2
      src/package.nls.ca.json
  12. 1 2
      src/package.nls.de.json
  13. 1 2
      src/package.nls.es.json
  14. 1 2
      src/package.nls.fr.json
  15. 1 2
      src/package.nls.hi.json
  16. 1 2
      src/package.nls.id.json
  17. 1 2
      src/package.nls.it.json
  18. 1 2
      src/package.nls.ja.json
  19. 1 2
      src/package.nls.json
  20. 1 2
      src/package.nls.ko.json
  21. 1 2
      src/package.nls.nl.json
  22. 1 2
      src/package.nls.pl.json
  23. 1 2
      src/package.nls.pt-BR.json
  24. 1 2
      src/package.nls.ru.json
  25. 1 2
      src/package.nls.tr.json
  26. 1 2
      src/package.nls.vi.json
  27. 1 2
      src/package.nls.zh-CN.json
  28. 1 2
      src/package.nls.zh-TW.json
  29. 12 0
      src/shared/__tests__/experiments.spec.ts
  30. 2 0
      src/shared/experiments.ts
  31. 66 39
      src/utils/__tests__/resolveToolProtocol.spec.ts
  32. 8 6
      src/utils/resolveToolProtocol.ts
  33. 0 14
      src/utils/toolProtocol.ts
  34. 2 0
      webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx
  35. 4 0
      webview-ui/src/i18n/locales/ca/settings.json
  36. 4 0
      webview-ui/src/i18n/locales/de/settings.json
  37. 4 0
      webview-ui/src/i18n/locales/en/settings.json
  38. 4 0
      webview-ui/src/i18n/locales/es/settings.json
  39. 4 0
      webview-ui/src/i18n/locales/fr/settings.json
  40. 4 0
      webview-ui/src/i18n/locales/hi/settings.json
  41. 4 0
      webview-ui/src/i18n/locales/id/settings.json
  42. 4 0
      webview-ui/src/i18n/locales/it/settings.json
  43. 4 0
      webview-ui/src/i18n/locales/ja/settings.json
  44. 4 0
      webview-ui/src/i18n/locales/ko/settings.json
  45. 4 0
      webview-ui/src/i18n/locales/nl/settings.json
  46. 4 0
      webview-ui/src/i18n/locales/pl/settings.json
  47. 4 0
      webview-ui/src/i18n/locales/pt-BR/settings.json
  48. 4 0
      webview-ui/src/i18n/locales/ru/settings.json
  49. 4 0
      webview-ui/src/i18n/locales/tr/settings.json
  50. 4 0
      webview-ui/src/i18n/locales/vi/settings.json
  51. 4 0
      webview-ui/src/i18n/locales/zh-CN/settings.json
  52. 4 0
      webview-ui/src/i18n/locales/zh-TW/settings.json

+ 0 - 12
packages/build/src/__tests__/index.test.ts

@@ -100,12 +100,6 @@ describe("generatePackageJson", () => {
 								default: "",
 								description: "%settings.customStoragePath.description%",
 							},
-							"roo-cline.toolProtocol": {
-								type: "string",
-								enum: ["xml", "native"],
-								default: "xml",
-								description: "%settings.toolProtocol.description%",
-							},
 						},
 					},
 				},
@@ -219,12 +213,6 @@ describe("generatePackageJson", () => {
 							default: "",
 							description: "%settings.customStoragePath.description%",
 						},
-						"roo-code-nightly.toolProtocol": {
-							type: "string",
-							enum: ["xml", "native"],
-							default: "xml",
-							description: "%settings.toolProtocol.description%",
-						},
 					},
 				},
 			},

+ 2 - 0
packages/types/src/experiment.ts

@@ -12,6 +12,7 @@ export const experimentIds = [
 	"preventFocusDisruption",
 	"imageGeneration",
 	"runSlashCommand",
+	"nativeToolCalling",
 ] as const
 
 export const experimentIdsSchema = z.enum(experimentIds)
@@ -28,6 +29,7 @@ export const experimentsSchema = z.object({
 	preventFocusDisruption: z.boolean().optional(),
 	imageGeneration: z.boolean().optional(),
 	runSlashCommand: z.boolean().optional(),
+	nativeToolCalling: z.boolean().optional(),
 })
 
 export type Experiments = z.infer<typeof experimentsSchema>

+ 13 - 11
src/core/assistant-message/presentAssistantMessage.ts

@@ -280,18 +280,18 @@ export async function presentAssistantMessage(cline: Task) {
 			// Track if we've already pushed a tool result for this tool call (native protocol only)
 			let hasToolResult = false
 
-			const pushToolResult = (content: ToolResponse) => {
-				// Check if we're using native tool protocol
-				const toolProtocol = resolveToolProtocol(
-					cline.apiConfiguration,
-					cline.api.getModel().info,
-					cline.apiConfiguration.apiProvider,
-				)
-				const isNative = isNativeProtocol(toolProtocol)
-
-				// Get the tool call ID if this is a native tool call
-				const toolCallId = (block as any).id
+			// Check if we're using native tool protocol (do this once before defining pushToolResult)
+			const state = await cline.providerRef.deref()?.getState()
+			const toolProtocol = resolveToolProtocol(
+				cline.apiConfiguration,
+				cline.api.getModel().info,
+				cline.apiConfiguration.apiProvider,
+				state?.experiments,
+			)
+			const isNative = isNativeProtocol(toolProtocol)
+			const toolCallId = (block as any).id
 
+			const pushToolResult = (content: ToolResponse) => {
 				if (isNative && toolCallId) {
 					// For native protocol, only allow ONE tool_result per tool call
 					if (hasToolResult) {
@@ -518,10 +518,12 @@ export async function presentAssistantMessage(cline: Task) {
 					await checkpointSaveAndMark(cline)
 
 					// Check if native protocol is enabled - if so, always use single-file class-based tool
+					const state = await cline.providerRef.deref()?.getState()
 					const applyDiffToolProtocol = resolveToolProtocol(
 						cline.apiConfiguration,
 						cline.api.getModel().info,
 						cline.apiConfiguration.apiProvider,
+						state?.experiments,
 					)
 					if (isNativeProtocol(applyDiffToolProtocol)) {
 						await applyDiffToolClass.handle(cline, block as ToolUse<"apply_diff">, {

+ 3 - 4
src/core/prompts/responses.ts

@@ -3,8 +3,7 @@ import * as path from "path"
 import * as diff from "diff"
 import { RooIgnoreController, LOCK_TEXT_SYMBOL } from "../ignore/RooIgnoreController"
 import { RooProtectedController } from "../protect/RooProtectedController"
-import { ToolProtocol, isNativeProtocol } from "@roo-code/types"
-import { getToolProtocolFromSettings } from "../../utils/toolProtocol"
+import { ToolProtocol, isNativeProtocol, TOOL_PROTOCOL } from "@roo-code/types"
 
 export const formatResponse = {
 	toolDenied: () => `The user denied this operation.`,
@@ -245,10 +244,10 @@ Always ensure you provide all required parameters for the tool you wish to use.`
 /**
  * Gets the appropriate tool use instructions reminder based on the protocol.
  *
- * @param protocol - Optional tool protocol, falls back to default if not provided
+ * @param protocol - Optional tool protocol, defaults to XML if not provided
  * @returns The tool use instructions reminder text
  */
 function getToolInstructionsReminder(protocol?: ToolProtocol): string {
-	const effectiveProtocol = protocol ?? getToolProtocolFromSettings()
+	const effectiveProtocol = protocol ?? TOOL_PROTOCOL.XML
 	return isNativeProtocol(effectiveProtocol) ? toolUseInstructionsReminderNative : toolUseInstructionsReminder
 }

+ 31 - 5
src/core/task/Task.ts

@@ -10,7 +10,7 @@ import delay from "delay"
 import pWaitFor from "p-wait-for"
 import { serializeError } from "serialize-error"
 import { Package } from "../../shared/package"
-import { getCurrentToolProtocol, formatToolInvocation } from "../tools/helpers/toolResultFormatting"
+import { formatToolInvocation } from "../tools/helpers/toolResultFormatting"
 
 import {
 	type TaskLike,
@@ -46,7 +46,6 @@ import {
 } from "@roo-code/types"
 import { TelemetryService } from "@roo-code/telemetry"
 import { CloudService, BridgeOrchestrator } from "@roo-code/cloud"
-import { getToolProtocolFromSettings } from "../../utils/toolProtocol"
 import { resolveToolProtocol } from "../../utils/resolveToolProtocol"
 
 // api
@@ -321,6 +320,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 		task,
 		images,
 		historyItem,
+		experiments: experimentsConfig,
 		startTask = true,
 		rootTask,
 		parentTask,
@@ -410,10 +410,12 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 
 		// Initialize the assistant message parser only for XML protocol.
 		// For native protocol, tool calls come as tool_call chunks, not XML.
+		// experiments is always provided via TaskOptions (defaults to experimentDefault in provider)
 		const toolProtocol = resolveToolProtocol(
 			this.apiConfiguration,
 			this.api.getModel().info,
 			this.apiConfiguration.apiProvider,
+			experimentsConfig,
 		)
 		this.assistantMessageParser = toolProtocol === "xml" ? new AssistantMessageParser() : undefined
 
@@ -1268,7 +1270,13 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 			} without value for required parameter '${paramName}'. Retrying...`,
 		)
 		const modelInfo = this.api.getModel().info
-		const toolProtocol = resolveToolProtocol(this.apiConfiguration, modelInfo, this.apiConfiguration.apiProvider)
+		const state = await this.providerRef.deref()?.getState()
+		const toolProtocol = resolveToolProtocol(
+			this.apiConfiguration,
+			modelInfo,
+			this.apiConfiguration.apiProvider,
+			state?.experiments,
+		)
 		return formatResponse.toolError(formatResponse.missingToolParameterError(paramName, toolProtocol))
 	}
 
@@ -1409,10 +1417,12 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 
 		// v2.0 xml tags refactor caveat: since we don't use tools anymore, we need to replace all tool use blocks with a text block since the API disallows conversations with tool uses and no tool schema
 		// Now also protocol-aware: format according to current protocol setting
+		const state = await this.providerRef.deref()?.getState()
 		const protocol = resolveToolProtocol(
 			this.apiConfiguration,
 			this.api.getModel().info,
 			this.apiConfiguration.apiProvider,
+			state?.experiments,
 		)
 		const useNative = isNativeProtocol(protocol)
 
@@ -1786,10 +1796,12 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 				break
 			} else {
 				const modelInfo = this.api.getModel().info
+				const state = await this.providerRef.deref()?.getState()
 				const toolProtocol = resolveToolProtocol(
 					this.apiConfiguration,
 					modelInfo,
 					this.apiConfiguration.apiProvider,
+					state?.experiments,
 				)
 				nextUserContent = [{ type: "text", text: formatResponse.noToolsUsed(toolProtocol) }]
 				this.consecutiveMistakeCount++
@@ -2436,11 +2448,13 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 					const parsedBlocks = this.assistantMessageParser.getContentBlocks()
 
 					// Check if we're using native protocol
+					const state = await this.providerRef.deref()?.getState()
 					const isNative = isNativeProtocol(
 						resolveToolProtocol(
 							this.apiConfiguration,
 							this.api.getModel().info,
 							this.apiConfiguration.apiProvider,
+							state?.experiments,
 						),
 					)
 
@@ -2581,10 +2595,12 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 
 					if (!didToolUse) {
 						const modelInfo = this.api.getModel().info
+						const state = await this.providerRef.deref()?.getState()
 						const toolProtocol = resolveToolProtocol(
 							this.apiConfiguration,
 							modelInfo,
 							this.apiConfiguration.apiProvider,
+							state?.experiments,
 						)
 						this.userMessageContent.push({ type: "text", text: formatResponse.noToolsUsed(toolProtocol) })
 						this.consecutiveMistakeCount++
@@ -2610,12 +2626,14 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 					// apiConversationHistory at line 1876. Since the assistant failed to respond,
 					// we need to remove that message before retrying to avoid having two consecutive
 					// user messages (which would cause tool_result validation errors).
+					let state = await this.providerRef.deref()?.getState()
 					if (
 						isNativeProtocol(
 							resolveToolProtocol(
 								this.apiConfiguration,
 								this.api.getModel().info,
 								this.apiConfiguration.apiProvider,
+								state?.experiments,
 							),
 						) &&
 						this.apiConversationHistory.length > 0
@@ -2628,7 +2646,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 					}
 
 					// Check if we should auto-retry or prompt the user
-					const state = await this.providerRef.deref()?.getState()
+					// Reuse the state variable from above
 					if (state?.autoApprovalEnabled && state?.alwaysApproveResubmit) {
 						// Auto-retry with backoff - don't persist failure message when retrying
 						const errorMsg =
@@ -2681,12 +2699,14 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 						} else {
 							// User declined to retry
 							// For native protocol, re-add the user message we removed
+							// Reuse the state variable from above
 							if (
 								isNativeProtocol(
 									resolveToolProtocol(
 										this.apiConfiguration,
 										this.api.getModel().info,
 										this.apiConfiguration.apiProvider,
+										state?.experiments,
 									),
 								)
 							) {
@@ -2791,6 +2811,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 				apiConfiguration ?? this.apiConfiguration,
 				modelInfo,
 				(apiConfiguration ?? this.apiConfiguration)?.apiProvider,
+				experiments,
 			)
 
 			return SYSTEM_PROMPT(
@@ -3030,7 +3051,12 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 		// 1. Tool protocol is set to NATIVE
 		// 2. Model supports native tools
 		const modelInfo = this.api.getModel().info
-		const toolProtocol = resolveToolProtocol(this.apiConfiguration, modelInfo, this.apiConfiguration.apiProvider)
+		const toolProtocol = resolveToolProtocol(
+			this.apiConfiguration,
+			modelInfo,
+			this.apiConfiguration.apiProvider,
+			state?.experiments,
+		)
 		const shouldIncludeTools = toolProtocol === TOOL_PROTOCOL.NATIVE && (modelInfo.supportsNativeTools ?? false)
 
 		// Build complete tools array: native tools + dynamic MCP tools, filtered by mode restrictions

+ 4 - 3
src/core/tools/MultiApplyDiffTool.ts

@@ -62,10 +62,13 @@ export async function applyDiffTool(
 	removeClosingTag: RemoveClosingTag,
 ) {
 	// Check if native protocol is enabled - if so, always use single-file class-based tool
+	const provider = cline.providerRef.deref()
+	const state = await provider?.getState()
 	const toolProtocol = resolveToolProtocol(
 		cline.apiConfiguration,
 		cline.api.getModel().info,
 		cline.apiConfiguration.apiProvider,
+		state?.experiments,
 	)
 	if (isNativeProtocol(toolProtocol)) {
 		return applyDiffToolClass.handle(cline, block as ToolUse<"apply_diff">, {
@@ -77,9 +80,7 @@ export async function applyDiffTool(
 	}
 
 	// Check if MULTI_FILE_APPLY_DIFF experiment is enabled
-	const provider = cline.providerRef.deref()
-	if (provider) {
-		const state = await provider.getState()
+	if (provider && state) {
 		const isMultiFileApplyDiffEnabled = experiments.isEnabled(
 			state.experiments ?? {},
 			EXPERIMENT_IDS.MULTI_FILE_APPLY_DIFF,

+ 7 - 1
src/core/tools/ReadFileTool.ts

@@ -109,7 +109,13 @@ export class ReadFileTool extends BaseTool<"read_file"> {
 		const { handleError, pushToolResult } = callbacks
 		const fileEntries = params.files
 		const modelInfo = task.api.getModel().info
-		const protocol = resolveToolProtocol(task.apiConfiguration, modelInfo, task.apiConfiguration.apiProvider)
+		const state = await task.providerRef.deref()?.getState()
+		const protocol = resolveToolProtocol(
+			task.apiConfiguration,
+			modelInfo,
+			task.apiConfiguration.apiProvider,
+			state?.experiments,
+		)
 		const useNative = isNativeProtocol(protocol)
 
 		if (!fileEntries || fileEntries.length === 0) {

+ 1 - 6
src/core/tools/__tests__/applyDiffTool.experiment.spec.ts

@@ -156,12 +156,6 @@ describe("applyDiffTool experiment routing", () => {
 	})
 
 	it("should use class-based tool when native protocol is enabled regardless of experiment", async () => {
-		// Enable native protocol
-		const vscode = await import("vscode")
-		vi.mocked(vscode.workspace.getConfiguration).mockReturnValue({
-			get: vi.fn().mockReturnValue(TOOL_PROTOCOL.NATIVE),
-		} as any)
-
 		// Update model to support native tools
 		mockCline.api.getModel = vi.fn().mockReturnValue({
 			id: "test-model",
@@ -176,6 +170,7 @@ describe("applyDiffTool experiment routing", () => {
 		mockProvider.getState.mockResolvedValue({
 			experiments: {
 				[EXPERIMENT_IDS.MULTI_FILE_APPLY_DIFF]: true,
+				[EXPERIMENT_IDS.NATIVE_TOOL_CALLING]: true, // Enable native tool calling experiment
 			},
 		})
 		;(applyDiffToolClass.handle as any).mockResolvedValue(undefined)

+ 1 - 1
src/core/webview/generateSystemPrompt.ts

@@ -70,7 +70,7 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web
 	const canUseBrowserTool = modelSupportsBrowser && modeSupportsBrowser && (browserToolEnabled ?? true)
 
 	// Resolve tool protocol for system prompt generation
-	const toolProtocol = resolveToolProtocol(apiConfiguration, modelInfo, apiConfiguration.apiProvider)
+	const toolProtocol = resolveToolProtocol(apiConfiguration, modelInfo, apiConfiguration.apiProvider, experiments)
 
 	const systemPrompt = await SYSTEM_PROMPT(
 		provider.context,

+ 0 - 9
src/package.json

@@ -436,15 +436,6 @@
 					"minimum": 1,
 					"maximum": 200,
 					"description": "%settings.codeIndex.embeddingBatchSize.description%"
-				},
-				"roo-cline.toolProtocol": {
-					"type": "string",
-					"enum": [
-						"xml",
-						"native"
-					],
-					"default": "xml",
-					"description": "%settings.toolProtocol.description%"
 				}
 			}
 		}

+ 1 - 2
src/package.nls.ca.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "Activa la càrrega de fitxers AGENTS.md per a regles específiques de l'agent (vegeu https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "Temps màxim en segons per esperar les respostes de l'API (0 = sense temps d'espera, 1-3600s, per defecte: 600s). Es recomanen valors més alts per a proveïdors locals com LM Studio i Ollama que poden necessitar més temps de processament.",
 	"settings.newTaskRequireTodos.description": "Requerir el paràmetre de tasques pendents quan es creïn noves tasques amb l'eina new_task",
-	"settings.codeIndex.embeddingBatchSize.description": "La mida del lot per a operacions d'incrustació durant la indexació de codi. Ajusta això segons els límits del teu proveïdor d'API. Per defecte és 60.",
-	"settings.toolProtocol.description": "Protocol d'eines a utilitzar per a les interaccions d'IA. XML és el protocol per defecte i recomanat. Natiu és experimental i pot ser que no funcioni amb tots els proveïdors."
+	"settings.codeIndex.embeddingBatchSize.description": "La mida del lot per a operacions d'incrustació durant la indexació de codi. Ajusta això segons els límits del teu proveïdor d'API. Per defecte és 60."
 }

+ 1 - 2
src/package.nls.de.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "Aktiviert das Laden von AGENTS.md-Dateien für agentenspezifische Regeln (siehe https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "Maximale Wartezeit in Sekunden auf API-Antworten (0 = kein Timeout, 1-3600s, Standard: 600s). Höhere Werte werden für lokale Anbieter wie LM Studio und Ollama empfohlen, die möglicherweise mehr Verarbeitungszeit benötigen.",
 	"settings.newTaskRequireTodos.description": "Todos-Parameter beim Erstellen neuer Aufgaben mit dem new_task-Tool erfordern",
-	"settings.codeIndex.embeddingBatchSize.description": "Die Batch-Größe für Embedding-Operationen während der Code-Indexierung. Passe dies an die Limits deines API-Anbieters an. Standard ist 60.",
-	"settings.toolProtocol.description": "Tool-Protokoll, das für KI-Interaktionen verwendet werden soll. XML ist das Standard- und empfohlene Protokoll. Nativ ist experimentell und funktioniert möglicherweise nicht mit allen Anbietern."
+	"settings.codeIndex.embeddingBatchSize.description": "Die Batch-Größe für Embedding-Operationen während der Code-Indexierung. Passe dies an die Limits deines API-Anbieters an. Standard ist 60."
 }

+ 1 - 2
src/package.nls.es.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "Habilita la carga de archivos AGENTS.md para reglas específicas del agente (ver https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "Tiempo máximo en segundos de espera para las respuestas de la API (0 = sin tiempo de espera, 1-3600s, por defecto: 600s). Se recomiendan valores más altos para proveedores locales como LM Studio y Ollama que puedan necesitar más tiempo de procesamiento.",
 	"settings.newTaskRequireTodos.description": "Requerir el parámetro todos al crear nuevas tareas con la herramienta new_task",
-	"settings.codeIndex.embeddingBatchSize.description": "El tamaño del lote para operaciones de embedding durante la indexación de código. Ajusta esto según los límites de tu proveedor de API. Por defecto es 60.",
-	"settings.toolProtocol.description": "Protocolo de herramienta a utilizar para las interacciones de IA. XML es el protocolo predeterminado y recomendado. Nativo es experimental y puede que no funcione con todos los proveedores."
+	"settings.codeIndex.embeddingBatchSize.description": "El tamaño del lote para operaciones de embedding durante la indexación de código. Ajusta esto según los límites de tu proveedor de API. Por defecto es 60."
 }

+ 1 - 2
src/package.nls.fr.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "Activer le chargement des fichiers AGENTS.md pour les règles spécifiques à l'agent (voir https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "Temps maximum en secondes d'attente pour les réponses de l'API (0 = pas de timeout, 1-3600s, par défaut : 600s). Des valeurs plus élevées sont recommandées pour les fournisseurs locaux comme LM Studio et Ollama qui peuvent nécessiter plus de temps de traitement.",
 	"settings.newTaskRequireTodos.description": "Exiger le paramètre todos lors de la création de nouvelles tâches avec l'outil new_task",
-	"settings.codeIndex.embeddingBatchSize.description": "La taille du lot pour les opérations d'embedding lors de l'indexation du code. Ajustez ceci selon les limites de votre fournisseur d'API. Par défaut, c'est 60.",
-	"settings.toolProtocol.description": "Protocole d'outil à utiliser pour les interactions AI. XML est le protocole par défaut et recommandé. Natif est expérimental et peut ne pas fonctionner avec tous les fournisseurs."
+	"settings.codeIndex.embeddingBatchSize.description": "La taille du lot pour les opérations d'embedding lors de l'indexation du code. Ajustez ceci selon les limites de votre fournisseur d'API. Par défaut, c'est 60."
 }

+ 1 - 2
src/package.nls.hi.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "एजेंट-विशिष्ट नियमों के लिए AGENTS.md फ़ाइलों को लोड करना सक्षम करें (देखें https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "एपीआई प्रतिक्रियाओं की प्रतीक्षा करने के लिए सेकंड में अधिकतम समय (0 = कोई टाइमआउट नहीं, 1-3600s, डिफ़ॉल्ट: 600s)। एलएम स्टूडियो और ओलामा जैसे स्थानीय प्रदाताओं के लिए उच्च मानों की सिफारिश की जाती है जिन्हें अधिक प्रसंस्करण समय की आवश्यकता हो सकती है।",
 	"settings.newTaskRequireTodos.description": "new_task टूल के साथ नए कार्य बनाते समय टूडू पैरामीटर की आवश्यकता होती है",
-	"settings.codeIndex.embeddingBatchSize.description": "कोड इंडेक्सिंग के दौरान एम्बेडिंग ऑपरेशन के लिए बैच साइज़। इसे अपने API प्रदाता की सीमाओं के अनुसार समायोजित करें। डिफ़ॉल्ट 60 है।",
-	"settings.toolProtocol.description": "एआई इंटरैक्शन के लिए उपयोग करने वाला टूल प्रोटोकॉल। एक्सएमएल डिफ़ॉल्ट और अनुशंसित प्रोटोकॉल है। नेटिव प्रायोगिक है और सभी प्रदाताओं के साथ काम नहीं कर सकता है।"
+	"settings.codeIndex.embeddingBatchSize.description": "कोड इंडेक्सिंग के दौरान एम्बेडिंग ऑपरेशन के लिए बैच साइज़। इसे अपने API प्रदाता की सीमाओं के अनुसार समायोजित करें। डिफ़ॉल्ट 60 है।"
 }

+ 1 - 2
src/package.nls.id.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "Aktifkan pemuatan file AGENTS.md untuk aturan khusus agen (lihat https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "Waktu maksimum dalam detik untuk menunggu respons API (0 = tidak ada batas waktu, 1-3600s, default: 600s). Nilai yang lebih tinggi disarankan untuk penyedia lokal seperti LM Studio dan Ollama yang mungkin memerlukan lebih banyak waktu pemrosesan.",
 	"settings.newTaskRequireTodos.description": "Memerlukan parameter todos saat membuat tugas baru dengan alat new_task",
-	"settings.codeIndex.embeddingBatchSize.description": "Ukuran batch untuk operasi embedding selama pengindeksan kode. Sesuaikan ini berdasarkan batas penyedia API kamu. Default adalah 60.",
-	"settings.toolProtocol.description": "Protokol alat untuk digunakan untuk interaksi AI. XML adalah protokol default dan yang direkomendasikan. Native bersifat eksperimental dan mungkin tidak berfungsi dengan semua penyedia."
+	"settings.codeIndex.embeddingBatchSize.description": "Ukuran batch untuk operasi embedding selama pengindeksan kode. Sesuaikan ini berdasarkan batas penyedia API kamu. Default adalah 60."
 }

+ 1 - 2
src/package.nls.it.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "Abilita il caricamento dei file AGENTS.md per regole specifiche dell'agente (vedi https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "Tempo massimo in secondi di attesa per le risposte API (0 = nessun timeout, 1-3600s, predefinito: 600s). Valori più alti sono consigliati per provider locali come LM Studio e Ollama che potrebbero richiedere più tempo di elaborazione.",
 	"settings.newTaskRequireTodos.description": "Richiedere il parametro todos quando si creano nuove attività con lo strumento new_task",
-	"settings.codeIndex.embeddingBatchSize.description": "La dimensione del batch per le operazioni di embedding durante l'indicizzazione del codice. Regola questo in base ai limiti del tuo provider API. Il valore predefinito è 60.",
-	"settings.toolProtocol.description": "Protocollo dello strumento da utilizzare per le interazioni AI. XML è il protocollo predefinito e consigliato. Nativo è sperimentale e potrebbe non funzionare con tutti i provider."
+	"settings.codeIndex.embeddingBatchSize.description": "La dimensione del batch per le operazioni di embedding durante l'indicizzazione del codice. Regola questo in base ai limiti del tuo provider API. Il valore predefinito è 60."
 }

+ 1 - 2
src/package.nls.ja.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "エージェント固有のルールのためにAGENTS.mdファイルの読み込みを有効にします(参照:https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "API応答を待機する最大時間(秒)(0 = タイムアウトなし、1-3600秒、デフォルト: 600秒)。LM StudioやOllamaのような、より多くの処理時間を必要とする可能性のあるローカルプロバイダーには、より高い値が推奨されます。",
 	"settings.newTaskRequireTodos.description": "new_taskツールで新しいタスクを作成する際にtodosパラメータを必須にする",
-	"settings.codeIndex.embeddingBatchSize.description": "コードインデックス作成中のエンベディング操作のバッチサイズ。APIプロバイダーの制限に基づいてこれを調整してください。デフォルトは60です。",
-	"settings.toolProtocol.description": "AIインタラクションに使用するツールプロトコル。XMLがデフォルトで推奨されるプロトコルです。ネイティブは実験的なものであり、すべてのプロバイダーで動作するとは限りません。"
+	"settings.codeIndex.embeddingBatchSize.description": "コードインデックス作成中のエンベディング操作のバッチサイズ。APIプロバイダーの制限に基づいてこれを調整してください。デフォルトは60です。"
 }

+ 1 - 2
src/package.nls.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "Enable loading of AGENTS.md files for agent-specific rules (see https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "Maximum time in seconds to wait for API responses (0 = no timeout, 1-3600s, default: 600s). Higher values are recommended for local providers like LM Studio and Ollama that may need more processing time.",
 	"settings.newTaskRequireTodos.description": "Require todos parameter when creating new tasks with the new_task tool",
-	"settings.codeIndex.embeddingBatchSize.description": "The batch size for embedding operations during code indexing. Adjust this based on your API provider's limits. Default is 60.",
-	"settings.toolProtocol.description": "Tool protocol to use for AI interactions. XML is the default and recommended protocol. Native is experimental and may not work with all providers."
+	"settings.codeIndex.embeddingBatchSize.description": "The batch size for embedding operations during code indexing. Adjust this based on your API provider's limits. Default is 60."
 }

+ 1 - 2
src/package.nls.ko.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "에이전트별 규칙에 대한 AGENTS.md 파일 로드를 활성화합니다 (참조: https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "API 응답을 기다리는 최대 시간(초) (0 = 시간 초과 없음, 1-3600초, 기본값: 600초). 더 많은 처리 시간이 필요할 수 있는 LM Studio 및 Ollama와 같은 로컬 공급자에게는 더 높은 값을 사용하는 것이 좋습니다.",
 	"settings.newTaskRequireTodos.description": "new_task 도구로 새 작업을 생성할 때 todos 매개변수 필요",
-	"settings.codeIndex.embeddingBatchSize.description": "코드 인덱싱 중 임베딩 작업의 배치 크기입니다. API 공급자의 제한에 따라 이를 조정하세요. 기본값은 60입니다.",
-	"settings.toolProtocol.description": "AI 상호 작용에 사용할 도구 프로토콜입니다. XML이 기본 권장 프로토콜입니다. 네이티브는 실험적이며 모든 공급자와 작동하지 않을 수 있습니다."
+	"settings.codeIndex.embeddingBatchSize.description": "코드 인덱싱 중 임베딩 작업의 배치 크기입니다. API 공급자의 제한에 따라 이를 조정하세요. 기본값은 60입니다."
 }

+ 1 - 2
src/package.nls.nl.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "Laden van AGENTS.md-bestanden voor agentspecifieke regels inschakelen (zie https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "Maximale tijd in seconden om te wachten op API-reacties (0 = geen time-out, 1-3600s, standaard: 600s). Hogere waarden worden aanbevolen voor lokale providers zoals LM Studio en Ollama die mogelijk meer verwerkingstijd nodig hebben.",
 	"settings.newTaskRequireTodos.description": "Todos-parameter vereisen bij het maken van nieuwe taken met de new_task tool",
-	"settings.codeIndex.embeddingBatchSize.description": "De batchgrootte voor embedding-operaties tijdens code-indexering. Pas dit aan op basis van de limieten van je API-provider. Standaard is 60.",
-	"settings.toolProtocol.description": "Toolprotocol te gebruiken voor AI-interacties. XML is het standaard en aanbevolen protocol. Native is experimenteel en werkt mogelijk niet met alle providers."
+	"settings.codeIndex.embeddingBatchSize.description": "De batchgrootte voor embedding-operaties tijdens code-indexering. Pas dit aan op basis van de limieten van je API-provider. Standaard is 60."
 }

+ 1 - 2
src/package.nls.pl.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "Włącz wczytywanie plików AGENTS.md dla reguł specyficznych dla agenta (zobacz https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "Maksymalny czas w sekundach oczekiwania na odpowiedzi API (0 = brak limitu czasu, 1-3600s, domyślnie: 600s). Wyższe wartości są zalecane dla lokalnych dostawców, takich jak LM Studio i Ollama, którzy mogą potrzebować więcej czasu na przetwarzanie.",
 	"settings.newTaskRequireTodos.description": "Wymagaj parametru todos podczas tworzenia nowych zadań za pomocą narzędzia new_task",
-	"settings.codeIndex.embeddingBatchSize.description": "Rozmiar partii dla operacji osadzania podczas indeksowania kodu. Dostosuj to w oparciu o limity twojego dostawcy API. Domyślnie to 60.",
-	"settings.toolProtocol.description": "Protokół narzędzi do użycia w interakcjach z AI. XML jest domyślnym i zalecanym protokołem. Natywny jest eksperymentalny i może nie działać ze wszystkimi dostawcami."
+	"settings.codeIndex.embeddingBatchSize.description": "Rozmiar partii dla operacji osadzania podczas indeksowania kodu. Dostosuj to w oparciu o limity twojego dostawcy API. Domyślnie to 60."
 }

+ 1 - 2
src/package.nls.pt-BR.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "Habilita o carregamento de arquivos AGENTS.md para regras específicas do agente (consulte https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "Tempo máximo em segundos de espera pelas respostas da API (0 = sem tempo limite, 1-3600s, padrão: 600s). Valores mais altos são recomendados para provedores locais como LM Studio e Ollama que podem precisar de mais tempo de processamento.",
 	"settings.newTaskRequireTodos.description": "Exigir parâmetro todos ao criar novas tarefas com a ferramenta new_task",
-	"settings.codeIndex.embeddingBatchSize.description": "O tamanho do lote para operações de embedding durante a indexação de código. Ajuste isso com base nos limites do seu provedor de API. O padrão é 60.",
-	"settings.toolProtocol.description": "Protocolo de ferramenta a ser usado para interações de IA. XML é o protocolo padrão e recomendado. Nativo é experimental e pode não funcionar com todos os provedores."
+	"settings.codeIndex.embeddingBatchSize.description": "O tamanho do lote para operações de embedding durante a indexação de código. Ajuste isso com base nos limites do seu provedor de API. O padrão é 60."
 }

+ 1 - 2
src/package.nls.ru.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "Включить загрузку файлов AGENTS.md для специфичных для агента правил (см. https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "Максимальное время в секундах для ожидания ответов API (0 = нет тайм-аута, 1-3600 с, по умолчанию: 600 с). Рекомендуются более высокие значения для локальных провайдеров, таких как LM Studio и Ollama, которым может потребоваться больше времени на обработку.",
 	"settings.newTaskRequireTodos.description": "Требовать параметр todos при создании новых задач с помощью инструмента new_task",
-	"settings.codeIndex.embeddingBatchSize.description": "Размер пакета для операций встраивания во время индексации кода. Настройте это в соответствии с ограничениями вашего API-провайдера. По умолчанию 60.",
-	"settings.toolProtocol.description": "Протокол инструментов для использования в взаимодействиях с ИИ. XML является протоколом по умолчанию и рекомендуемым. Нативный является экспериментальным и может не работать со всеми провайдерами."
+	"settings.codeIndex.embeddingBatchSize.description": "Размер пакета для операций встраивания во время индексации кода. Настройте это в соответствии с ограничениями вашего API-провайдера. По умолчанию 60."
 }

+ 1 - 2
src/package.nls.tr.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "Aracıya özgü kurallar için AGENTS.md dosyalarının yüklenmesini etkinleştirin (bkz. https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "API yanıtları için beklenecek maksimum süre (saniye cinsinden) (0 = zaman aşımı yok, 1-3600s, varsayılan: 600s). LM Studio ve Ollama gibi daha fazla işlem süresi gerektirebilecek yerel sağlayıcılar için daha yüksek değerler önerilir.",
 	"settings.newTaskRequireTodos.description": "new_task aracıyla yeni görevler oluştururken todos parametresini gerekli kıl",
-	"settings.codeIndex.embeddingBatchSize.description": "Kod indeksleme sırasında gömme işlemleri için toplu iş boyutu. Bunu API sağlayıcınızın sınırlarına göre ayarlayın. Varsayılan 60'tır.",
-	"settings.toolProtocol.description": "Yapay zeka etkileşimleri için kullanılacak araç protokolü. XML, varsayılan ve önerilen protokoldür. Yerel deneyseldir ve tüm sağlayıcılarla çalışmayabilir."
+	"settings.codeIndex.embeddingBatchSize.description": "Kod indeksleme sırasında gömme işlemleri için toplu iş boyutu. Bunu API sağlayıcınızın sınırlarına göre ayarlayın. Varsayılan 60'tır."
 }

+ 1 - 2
src/package.nls.vi.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "Bật tải tệp AGENTS.md cho các quy tắc dành riêng cho tác nhân (xem https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "Thời gian tối đa tính bằng giây để đợi phản hồi API (0 = không có thời gian chờ, 1-3600 giây, mặc định: 600 giây). Nên sử dụng các giá trị cao hơn cho các nhà cung cấp cục bộ như LM Studio và Ollama có thể cần thêm thời gian xử lý.",
 	"settings.newTaskRequireTodos.description": "Yêu cầu tham số todos khi tạo nhiệm vụ mới với công cụ new_task",
-	"settings.codeIndex.embeddingBatchSize.description": "Kích thước lô cho các hoạt động nhúng trong quá trình lập chỉ mục mã. Điều chỉnh điều này dựa trên giới hạn của nhà cung cấp API của bạn. Mặc định là 60.",
-	"settings.toolProtocol.description": "Giao thức công cụ để sử dụng cho các tương tác AI. XML là giao thức mặc định và được khuyến nghị. Bản gốc là thử nghiệm và có thể không hoạt động với tất cả các nhà cung cấp."
+	"settings.codeIndex.embeddingBatchSize.description": "Kích thước lô cho các hoạt động nhúng trong quá trình lập chỉ mục mã. Điều chỉnh điều này dựa trên giới hạn của nhà cung cấp API của bạn. Mặc định là 60."
 }

+ 1 - 2
src/package.nls.zh-CN.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "为特定于代理的规则启用 AGENTS.md 文件的加载(请参阅 https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "等待 API 响应的最长时间(秒)(0 = 无超时,1-3600秒,默认值:600秒)。对于像 LM Studio 和 Ollama 这样可能需要更多处理时间的本地提供商,建议使用更高的值。",
 	"settings.newTaskRequireTodos.description": "使用 new_task 工具创建新任务时需要 todos 参数",
-	"settings.codeIndex.embeddingBatchSize.description": "代码索引期间嵌入操作的批处理大小。根据 API 提供商的限制调整此设置。默认值为 60。",
-	"settings.toolProtocol.description": "用于 AI 交互的工具协议。XML 是默认且推荐的协议。本机是实验性的,可能不适用于所有提供商。"
+	"settings.codeIndex.embeddingBatchSize.description": "代码索引期间嵌入操作的批处理大小。根据 API 提供商的限制调整此设置。默认值为 60。"
 }

+ 1 - 2
src/package.nls.zh-TW.json

@@ -43,6 +43,5 @@
 	"settings.useAgentRules.description": "為特定於代理的規則啟用 AGENTS.md 檔案的載入(請參閱 https://agent-rules.org/)",
 	"settings.apiRequestTimeout.description": "等待 API 回應的最長時間(秒)(0 = 無超時,1-3600秒,預設值:600秒)。對於像 LM Studio 和 Ollama 這樣可能需要更多處理時間的本地提供商,建議使用更高的值。",
 	"settings.newTaskRequireTodos.description": "使用 new_task 工具建立新工作時需要 todos 參數",
-	"settings.codeIndex.embeddingBatchSize.description": "程式碼索引期間嵌入操作的批次大小。根據 API 提供商的限制調整此設定。預設值為 60。",
-	"settings.toolProtocol.description": "用於 AI 互動的工具協議。XML 是預設且推薦的協議。本機是實驗性的,可能不適用於所有提供商。"
+	"settings.codeIndex.embeddingBatchSize.description": "程式碼索引期間嵌入操作的批次大小。根據 API 提供商的限制調整此設定。預設值為 60。"
 }

+ 12 - 0
src/shared/__tests__/experiments.spec.ts

@@ -23,6 +23,15 @@ describe("experiments", () => {
 		})
 	})
 
+	describe("NATIVE_TOOL_CALLING", () => {
+		it("is configured correctly", () => {
+			expect(EXPERIMENT_IDS.NATIVE_TOOL_CALLING).toBe("nativeToolCalling")
+			expect(experimentConfigsMap.NATIVE_TOOL_CALLING).toMatchObject({
+				enabled: false,
+			})
+		})
+	})
+
 	describe("isEnabled", () => {
 		it("returns false when POWER_STEERING experiment is not enabled", () => {
 			const experiments: Record<ExperimentId, boolean> = {
@@ -31,6 +40,7 @@ describe("experiments", () => {
 				preventFocusDisruption: false,
 				imageGeneration: false,
 				runSlashCommand: false,
+				nativeToolCalling: false,
 			}
 			expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
 		})
@@ -42,6 +52,7 @@ describe("experiments", () => {
 				preventFocusDisruption: false,
 				imageGeneration: false,
 				runSlashCommand: false,
+				nativeToolCalling: false,
 			}
 			expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true)
 		})
@@ -53,6 +64,7 @@ describe("experiments", () => {
 				preventFocusDisruption: false,
 				imageGeneration: false,
 				runSlashCommand: false,
+				nativeToolCalling: false,
 			}
 			expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
 		})

+ 2 - 0
src/shared/experiments.ts

@@ -6,6 +6,7 @@ export const EXPERIMENT_IDS = {
 	PREVENT_FOCUS_DISRUPTION: "preventFocusDisruption",
 	IMAGE_GENERATION: "imageGeneration",
 	RUN_SLASH_COMMAND: "runSlashCommand",
+	NATIVE_TOOL_CALLING: "nativeToolCalling",
 } as const satisfies Record<string, ExperimentId>
 
 type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPERIMENT_IDS>>>
@@ -22,6 +23,7 @@ export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
 	PREVENT_FOCUS_DISRUPTION: { enabled: false },
 	IMAGE_GENERATION: { enabled: false },
 	RUN_SLASH_COMMAND: { enabled: false },
+	NATIVE_TOOL_CALLING: { enabled: false },
 }
 
 export const experimentDefault = Object.fromEntries(

+ 66 - 39
src/utils/__tests__/resolveToolProtocol.spec.ts

@@ -1,20 +1,9 @@
-import { describe, it, expect, vi, beforeEach } from "vitest"
+import { describe, it, expect } from "vitest"
 import { resolveToolProtocol } from "../resolveToolProtocol"
 import { TOOL_PROTOCOL } from "@roo-code/types"
-import type { ProviderSettings, ModelInfo, ProviderName } from "@roo-code/types"
-import * as toolProtocolModule from "../toolProtocol"
-
-// Mock the getToolProtocolFromSettings function
-vi.mock("../toolProtocol", () => ({
-	getToolProtocolFromSettings: vi.fn(() => "xml"),
-}))
+import type { ProviderSettings, ModelInfo, Experiments } from "@roo-code/types"
 
 describe("resolveToolProtocol", () => {
-	beforeEach(() => {
-		// Reset mock before each test
-		vi.mocked(toolProtocolModule.getToolProtocolFromSettings).mockReturnValue("xml")
-	})
-
 	describe("Precedence Level 1: User Profile Setting", () => {
 		it("should use profile toolProtocol when explicitly set to xml", () => {
 			const settings: ProviderSettings = {
@@ -71,9 +60,8 @@ describe("resolveToolProtocol", () => {
 		})
 	})
 
-	describe("Precedence Level 2: Global User Preference (VSCode Setting)", () => {
-		it("should use global setting when no profile setting", () => {
-			vi.mocked(toolProtocolModule.getToolProtocolFromSettings).mockReturnValue("native")
+	describe("Precedence Level 2: Experimental Setting (Native Tool Calling)", () => {
+		it("should use experimental setting when enabled and no profile setting", () => {
 			const settings: ProviderSettings = {
 				apiProvider: "roo",
 			}
@@ -83,12 +71,14 @@ describe("resolveToolProtocol", () => {
 				supportsPromptCache: false,
 				supportsNativeTools: true, // Model supports native tools
 			}
-			const result = resolveToolProtocol(settings, modelInfo, "roo")
-			expect(result).toBe(TOOL_PROTOCOL.NATIVE) // Global setting wins over provider default
+			const experiments: Experiments = {
+				nativeToolCalling: true, // Experimental setting enabled
+			}
+			const result = resolveToolProtocol(settings, modelInfo, "roo", experiments)
+			expect(result).toBe(TOOL_PROTOCOL.NATIVE) // Experimental setting wins over provider default
 		})
 
-		it("should use global setting over model default", () => {
-			vi.mocked(toolProtocolModule.getToolProtocolFromSettings).mockReturnValue("native")
+		it("should use experimental setting over model default", () => {
 			const settings: ProviderSettings = {
 				apiProvider: "roo",
 			}
@@ -99,14 +89,48 @@ describe("resolveToolProtocol", () => {
 				defaultToolProtocol: "xml", // Model prefers XML
 				supportsNativeTools: true, // But model supports native tools
 			}
-			const result = resolveToolProtocol(settings, modelInfo, "roo")
-			expect(result).toBe(TOOL_PROTOCOL.NATIVE) // Global setting wins
+			const experiments: Experiments = {
+				nativeToolCalling: true, // Experimental setting enabled
+			}
+			const result = resolveToolProtocol(settings, modelInfo, "roo", experiments)
+			expect(result).toBe(TOOL_PROTOCOL.NATIVE) // Experimental setting wins
+		})
+
+		it("should not use experimental setting when disabled", () => {
+			const settings: ProviderSettings = {
+				apiProvider: "roo",
+			}
+			const modelInfo: ModelInfo = {
+				maxTokens: 4096,
+				contextWindow: 128000,
+				supportsPromptCache: false,
+				supportsNativeTools: true,
+			}
+			const experiments: Experiments = {
+				nativeToolCalling: false, // Experimental setting disabled
+			}
+			const result = resolveToolProtocol(settings, modelInfo, "roo", experiments)
+			expect(result).toBe(TOOL_PROTOCOL.XML) // Falls back to provider default
+		})
+
+		it("should not use experimental setting when undefined", () => {
+			const settings: ProviderSettings = {
+				apiProvider: "roo",
+			}
+			const modelInfo: ModelInfo = {
+				maxTokens: 4096,
+				contextWindow: 128000,
+				supportsPromptCache: false,
+				supportsNativeTools: true,
+			}
+			const experiments: Experiments = {} // nativeToolCalling not defined
+			const result = resolveToolProtocol(settings, modelInfo, "roo", experiments)
+			expect(result).toBe(TOOL_PROTOCOL.XML) // Falls back to provider default
 		})
 	})
 
 	describe("Precedence Level 3: Model Default", () => {
-		it("should use model defaultToolProtocol when no profile or global setting", () => {
-			vi.mocked(toolProtocolModule.getToolProtocolFromSettings).mockReturnValue("xml")
+		it("should use model defaultToolProtocol when no profile or experimental setting", () => {
 			const settings: ProviderSettings = {
 				apiProvider: "roo",
 			}
@@ -118,7 +142,7 @@ describe("resolveToolProtocol", () => {
 				supportsNativeTools: true, // Model must support native tools
 			}
 			const result = resolveToolProtocol(settings, modelInfo, "roo")
-			expect(result).toBe(TOOL_PROTOCOL.NATIVE) // Model default wins when global is XML (default)
+			expect(result).toBe(TOOL_PROTOCOL.NATIVE) // Model default wins when experiment is disabled
 		})
 
 		it("should override model capability when model default is present", () => {
@@ -271,7 +295,6 @@ describe("resolveToolProtocol", () => {
 
 	describe("Precedence Level 5: XML Fallback", () => {
 		it("should use XML fallback when no provider is specified and no preferences", () => {
-			vi.mocked(toolProtocolModule.getToolProtocolFromSettings).mockReturnValue("xml")
 			const settings: ProviderSettings = {}
 			const result = resolveToolProtocol(settings, undefined, undefined)
 			expect(result).toBe(TOOL_PROTOCOL.XML) // XML fallback
@@ -279,10 +302,8 @@ describe("resolveToolProtocol", () => {
 	})
 
 	describe("Complete Precedence Chain", () => {
-		it("should respect full precedence: Profile > Model Default > Model Capability > Provider > Global", () => {
+		it("should respect full precedence: Profile > Experimental > Model Default > Provider", () => {
 			// Set up a scenario with all levels defined
-			vi.mocked(toolProtocolModule.getToolProtocolFromSettings).mockReturnValue("xml")
-
 			const settings: ProviderSettings = {
 				toolProtocol: "native", // Level 1: User profile setting
 				apiProvider: "roo",
@@ -292,14 +313,18 @@ describe("resolveToolProtocol", () => {
 				maxTokens: 4096,
 				contextWindow: 128000,
 				supportsPromptCache: false,
-				defaultToolProtocol: "xml", // Level 2: Model default
-				supportsNativeTools: true, // Level 3: Model capability
+				defaultToolProtocol: "xml", // Level 3: Model default
+				supportsNativeTools: true, // Support check
 			}
 
-			// Level 4: Provider default would be "native" for roo
-			// Level 5: Global setting is "xml"
+			const experiments: Experiments = {
+				nativeToolCalling: false, // Level 2: Experimental setting (disabled)
+			}
 
-			const result = resolveToolProtocol(settings, modelInfo, "roo")
+			// Level 4: Provider default would be XML
+			// Level 5: XML fallback
+
+			const result = resolveToolProtocol(settings, modelInfo, "roo", experiments)
 			expect(result).toBe(TOOL_PROTOCOL.NATIVE) // Profile setting wins
 		})
 
@@ -345,10 +370,9 @@ describe("resolveToolProtocol", () => {
 			expect(result).toBe(TOOL_PROTOCOL.XML) // Provider default wins
 		})
 
-		it("should use global setting over provider default", () => {
-			vi.mocked(toolProtocolModule.getToolProtocolFromSettings).mockReturnValue("native")
+		it("should use experimental setting over provider default", () => {
 			const settings: ProviderSettings = {
-				apiProvider: "ollama", // Provider not in native list, defaults to XML
+				apiProvider: "ollama", // Provider defaults to XML
 			}
 			const modelInfo: ModelInfo = {
 				maxTokens: 4096,
@@ -356,9 +380,12 @@ describe("resolveToolProtocol", () => {
 				supportsPromptCache: false,
 				supportsNativeTools: true, // Model supports native tools
 			}
+			const experiments: Experiments = {
+				nativeToolCalling: true, // Experimental setting enabled
+			}
 
-			const result = resolveToolProtocol(settings, modelInfo, "ollama")
-			expect(result).toBe(TOOL_PROTOCOL.NATIVE) // Global setting wins over provider default
+			const result = resolveToolProtocol(settings, modelInfo, "ollama", experiments)
+			expect(result).toBe(TOOL_PROTOCOL.NATIVE) // Experimental setting wins over provider default
 		})
 	})
 

+ 8 - 6
src/utils/resolveToolProtocol.ts

@@ -1,13 +1,13 @@
-import { ToolProtocol, TOOL_PROTOCOL } from "@roo-code/types"
+import { ToolProtocol, TOOL_PROTOCOL, type Experiments } from "@roo-code/types"
 import type { ProviderSettings, ProviderName, ModelInfo } from "@roo-code/types"
-import { getToolProtocolFromSettings } from "./toolProtocol"
+import { EXPERIMENT_IDS, experiments } from "../shared/experiments"
 
 /**
  * Resolve the effective tool protocol based on the precedence hierarchy:
  * Support > Preference > Defaults
  *
  * 1. User Preference - Per-Profile (explicit profile setting)
- * 2. User Preference - Global (VSCode setting)
+ * 2. User Preference - Experimental Setting (nativeToolCalling experiment)
  * 3. Model Default (defaultToolProtocol in ModelInfo)
  * 4. Provider Default (XML by default, native for specific providers)
  * 5. XML Fallback (final fallback)
@@ -17,12 +17,14 @@ import { getToolProtocolFromSettings } from "./toolProtocol"
  * @param providerSettings - The provider settings for the current profile
  * @param modelInfo - Optional model information containing capabilities
  * @param provider - Optional provider name for provider-specific defaults
+ * @param experimentsConfig - Optional experiments configuration
  * @returns The resolved tool protocol (either "xml" or "native")
  */
 export function resolveToolProtocol(
 	providerSettings: ProviderSettings,
 	modelInfo?: ModelInfo,
 	provider?: ProviderName,
+	experimentsConfig?: Experiments,
 ): ToolProtocol {
 	let protocol: ToolProtocol
 
@@ -30,9 +32,9 @@ export function resolveToolProtocol(
 	if (providerSettings.toolProtocol) {
 		protocol = providerSettings.toolProtocol
 	}
-	// 2. User Preference - Global (VSCode global setting)
-	// Only treat as user preference if explicitly set to native (non-default value)
-	else if (getToolProtocolFromSettings() === TOOL_PROTOCOL.NATIVE) {
+	// 2. User Preference - Experimental Setting (nativeToolCalling experiment)
+	// Only treat as user preference if explicitly enabled
+	else if (experiments.isEnabled(experimentsConfig ?? {}, EXPERIMENT_IDS.NATIVE_TOOL_CALLING)) {
 		protocol = TOOL_PROTOCOL.NATIVE
 	}
 	// 3. Model Default - model's preferred protocol

+ 0 - 14
src/utils/toolProtocol.ts

@@ -1,14 +0,0 @@
-import * as vscode from "vscode"
-import { ToolProtocol } from "@roo-code/types"
-import { Package } from "../shared/package"
-
-/**
- * Get the tool protocol setting from VSCode configuration.
- * This centralizes the logic for retrieving the toolProtocol setting,
- * ensuring consistent behavior across the codebase.
- *
- * @returns The configured tool protocol, defaults to "xml" if not set
- */
-export function getToolProtocolFromSettings(): ToolProtocol {
-	return vscode.workspace.getConfiguration(Package.name).get<ToolProtocol>("toolProtocol", "xml")
-}

+ 2 - 0
webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx

@@ -237,6 +237,7 @@ describe("mergeExtensionState", () => {
 				newTaskRequireTodos: false,
 				imageGeneration: false,
 				runSlashCommand: false,
+				nativeToolCalling: false,
 			} as Record<ExperimentId, boolean>,
 			checkpointTimeout: DEFAULT_CHECKPOINT_TIMEOUT_SECONDS + 5,
 		}
@@ -258,6 +259,7 @@ describe("mergeExtensionState", () => {
 			newTaskRequireTodos: false,
 			imageGeneration: false,
 			runSlashCommand: false,
+			nativeToolCalling: false,
 		})
 	})
 })

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

@@ -771,6 +771,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "Habilitar comandes de barra diagonal iniciades pel model",
 			"description": "Quan està habilitat, Roo pot executar les vostres comandes de barra diagonal per executar fluxos de treball."
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "Habilitar crida d'eines nativa",
+			"description": "Quan està activat, Roo utilitzarà l'API de crida de funció nativa del proveïdor en lloc de definicions d'eines basades en XML. Això és experimental i encara no funciona amb tots els proveïdors."
 		}
 	},
 	"promptCaching": {

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

@@ -771,6 +771,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "Modellinitierte Slash-Befehle aktivieren",
 			"description": "Wenn aktiviert, kann Roo deine Slash-Befehle ausführen, um Workflows zu starten."
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "Native Tool Calling aktivieren",
+			"description": "Wenn aktiviert, verwendet Roo die native Function-Calling-API des Providers anstelle von XML-basierten Tool-Definitionen. Dies ist experimentell und funktioniert noch nicht mit allen Providern."
 		}
 	},
 	"promptCaching": {

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

@@ -776,6 +776,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "Enable model-initiated slash commands",
 			"description": "When enabled, Roo can run your slash commands to execute workflows."
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "Enable native tool calling",
+			"description": "When enabled, Roo will use the provider's native function calling API instead of XML-based tool definitions. This is experimental and does not yet work with all providers."
 		}
 	},
 	"promptCaching": {

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

@@ -771,6 +771,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "Habilitar comandos slash iniciados por el modelo",
 			"description": "Cuando está habilitado, Roo puede ejecutar tus comandos slash para ejecutar flujos de trabajo."
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "Habilitar llamada de herramientas nativas",
+			"description": "Cuando está habilitado, Roo utilizará la API de llamada de funciones nativa del proveedor en lugar de definiciones de herramientas basadas en XML. Esto es experimental y aún no funciona con todos los proveedores."
 		}
 	},
 	"promptCaching": {

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

@@ -771,6 +771,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "Activer les commandes slash initiées par le modèle",
 			"description": "Lorsque activé, Roo peut exécuter tes commandes slash pour lancer des workflows."
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "Activer l'appel natif d'outils",
+			"description": "Lorsqu'activé, Roo utilisera l'API native d'appel de fonction du fournisseur au lieu des définitions d'outils basées sur XML. Ceci est expérimental et ne fonctionne pas encore avec tous les fournisseurs."
 		}
 	},
 	"promptCaching": {

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

@@ -772,6 +772,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "मॉडल द्वारा शुरू किए गए स्लैश कमांड सक्षम करें",
 			"description": "जब सक्षम होता है, Roo वर्कफ़्लो चलाने के लिए आपके स्लैश कमांड चला सकता है।"
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "नेटिव टूल कॉलिंग सक्षम करें",
+			"description": "सक्षम होने पर, Roo XML-आधारित टूल परिभाषाओं के बजाय प्रदाता के नेटिव फ़ंक्शन कॉलिंग API का उपयोग करेगा। यह प्रयोगात्मक है और अभी सभी प्रदाताओं के साथ काम नहीं करता है।"
 		}
 	},
 	"promptCaching": {

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

@@ -801,6 +801,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "Aktifkan perintah slash yang dimulai model",
 			"description": "Ketika diaktifkan, Roo dapat menjalankan perintah slash Anda untuk mengeksekusi alur kerja."
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "Aktifkan pemanggilan alat asli",
+			"description": "Ketika diaktifkan, Roo akan menggunakan API pemanggilan fungsi asli dari penyedia alih-alih definisi alat berbasis XML. Ini bersifat eksperimental dan belum berfungsi dengan semua penyedia."
 		}
 	},
 	"promptCaching": {

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

@@ -772,6 +772,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "Abilita comandi slash avviati dal modello",
 			"description": "Quando abilitato, Roo può eseguire i tuoi comandi slash per eseguire flussi di lavoro."
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "Abilita chiamata nativa degli strumenti",
+			"description": "Quando abilitato, Roo utilizzerà l'API di chiamata di funzioni nativa del provider invece delle definizioni di strumenti basate su XML. Questo è sperimentale e non funziona ancora con tutti i provider."
 		}
 	},
 	"promptCaching": {

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

@@ -772,6 +772,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "モデル開始スラッシュコマンドを有効にする",
 			"description": "有効にすると、Rooがワークフローを実行するためにあなたのスラッシュコマンドを実行できます。"
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "ネイティブツール呼び出しを有効にする",
+			"description": "有効にすると、RooはXMLベースのツール定義の代わりにプロバイダーのネイティブ関数呼び出しAPIを使用します。これは実験的なもので、まだすべてのプロバイダーで機能するわけではありません。"
 		}
 	},
 	"promptCaching": {

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

@@ -772,6 +772,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "모델 시작 슬래시 명령 활성화",
 			"description": "활성화되면 Roo가 워크플로를 실행하기 위해 슬래시 명령을 실행할 수 있습니다."
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "네이티브 도구 호출 활성화",
+			"description": "활성화하면 Roo는 XML 기반 도구 정의 대신 제공자의 네이티브 함수 호출 API를 사용합니다. 이는 실험적이며 아직 모든 제공자에서 작동하지 않습니다."
 		}
 	},
 	"promptCaching": {

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

@@ -772,6 +772,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "Model-geïnitieerde slash-commando's inschakelen",
 			"description": "Wanneer ingeschakeld, kan Roo je slash-commando's uitvoeren om workflows uit te voeren."
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "Schakel native tool calling in",
+			"description": "Wanneer ingeschakeld, gebruikt Roo de native function calling API van de provider in plaats van XML-gebaseerde tooldefinities. Dit is experimenteel en werkt nog niet met alle providers."
 		}
 	},
 	"promptCaching": {

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

@@ -772,6 +772,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "Włącz polecenia slash inicjowane przez model",
 			"description": "Gdy włączone, Roo może uruchamiać twoje polecenia slash w celu wykonywania przepływów pracy."
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "Włącz natywne wywoływanie narzędzi",
+			"description": "Gdy włączone, Roo będzie używać natywnego API wywoływania funkcji dostawcy zamiast definicji narzędzi opartych na XML. Jest to funkcja eksperymentalna i nie działa jeszcze ze wszystkimi dostawcami."
 		}
 	},
 	"promptCaching": {

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

@@ -772,6 +772,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "Ativar comandos slash iniciados pelo modelo",
 			"description": "Quando ativado, Roo pode executar seus comandos slash para executar fluxos de trabalho."
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "Ativar chamada nativa de ferramentas",
+			"description": "Quando ativado, Roo usará a API de chamada de função nativa do provedor em vez de definições de ferramentas baseadas em XML. Isso é experimental e ainda não funciona com todos os provedores."
 		}
 	},
 	"promptCaching": {

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

@@ -772,6 +772,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "Включить слэш-команды, инициированные моделью",
 			"description": "Когда включено, Roo может выполнять ваши слэш-команды для выполнения рабочих процессов."
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "Включить нативный вызов инструментов",
+			"description": "Когда включено, Roo будет использовать нативный API вызова функций провайдера вместо XML-определений инструментов. Это экспериментальная функция и пока не работает со всеми провайдерами."
 		}
 	},
 	"promptCaching": {

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

@@ -772,6 +772,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "Model tarafından başlatılan slash komutlarını etkinleştir",
 			"description": "Etkinleştirildiğinde, Roo iş akışlarını yürütmek için slash komutlarınızı çalıştırabilir."
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "Native tool calling'i etkinleştir",
+			"description": "Etkinleştirildiğinde, Roo XML tabanlı araç tanımları yerine sağlayıcının yerel fonksiyon çağırma API'sını kullanacaktır. Bu deneyseldir ve henüz tüm sağlayıcılarla çalışmamaktadır."
 		}
 	},
 	"promptCaching": {

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

@@ -772,6 +772,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "Bật lệnh slash do mô hình khởi tạo",
 			"description": "Khi được bật, Roo có thể chạy các lệnh slash của bạn để thực hiện các quy trình làm việc."
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "Bật gọi công cụ gốc",
+			"description": "Khi được bật, Roo sẽ sử dụng API gọi hàm gốc của nhà cung cấp thay vì định nghĩa công cụ dựa trên XML. Đây là tính năng thử nghiệm và chưa hoạt động với tất cả các nhà cung cấp."
 		}
 	},
 	"promptCaching": {

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

@@ -772,6 +772,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "启用模型发起的斜杠命令",
 			"description": "启用后 Roo 可运行斜杠命令执行工作流程。"
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "启用原生工具调用",
+			"description": "启用后,Roo 将使用提供商的原生函数调用 API,而不是基于 XML 的工具定义。这是实验性功能,目前尚未支持所有提供商。"
 		}
 	},
 	"promptCaching": {

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

@@ -772,6 +772,10 @@
 		"RUN_SLASH_COMMAND": {
 			"name": "啟用模型啟動的斜線命令",
 			"description": "啟用時,Roo 可以執行您的斜線命令來執行工作流程。"
+		},
+		"NATIVE_TOOL_CALLING": {
+			"name": "啟用原生工具呼叫",
+			"description": "啟用後,Roo 將使用提供商的原生函式呼叫 API,而非基於 XML 的工具定義。這是實驗性功能,目前尚未支援所有提供商。"
 		}
 	},
 	"promptCaching": {