diff --git a/packages/types/src/message.ts b/packages/types/src/message.ts index dc0e3dfff5..b995d646a3 100644 --- a/packages/types/src/message.ts +++ b/packages/types/src/message.ts @@ -170,6 +170,7 @@ export const clineSays = [ "codebase_search_result", "user_edit_todos", "too_many_tools_warning", + "mode_switch_compatibility_warning", "tool", ] as const diff --git a/src/api/providers/deepseek.ts b/src/api/providers/deepseek.ts index 2e85c016b0..2d9cc6288b 100644 --- a/src/api/providers/deepseek.ts +++ b/src/api/providers/deepseek.ts @@ -1,6 +1,8 @@ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" +import { logger } from "../../utils/logging" + import { deepSeekModels, deepSeekDefaultModelId, @@ -15,6 +17,7 @@ import type { ApiHandlerOptions } from "../../shared/api" import { ApiStream, ApiStreamUsageChunk } from "../transform/stream" import { getModelParams } from "../transform/model-params" import { convertToR1Format } from "../transform/r1-format" +import { historyHasToolCallsWithoutReasoning } from "./utils/reasoning-history-guard" import { OpenAiHandler } from "./openai" import { extractReasoningFromDelta } from "./utils/extract-reasoning" @@ -130,12 +133,6 @@ export class DeepSeekHandler extends OpenAiHandler { const { info: modelInfo, temperature, reasoningEffort, maxTokens } = this.getModel() const isThinkingModel = isDeepSeekThinkingEnabled(modelId, this.options) - const thinking = supportsDeepSeekThinkingToggle(modelId) - ? ({ type: isThinkingModel ? "enabled" : "disabled" } as const) - : isThinkingModel - ? ({ type: "enabled" } as const) - : undefined - const deepSeekReasoningEffort = normalizeDeepSeekReasoningEffort(modelId, reasoningEffort) // Convert messages to R1 format (merges consecutive same-role messages) // This is required for DeepSeek which does not support successive messages with the same role @@ -147,9 +144,30 @@ export class DeepSeekHandler extends OpenAiHandler { mergeToolResultText: isThinkingModel, }) + // Layer 1 Guard: detect incompatible history (tool_calls without reasoning_content) + // and disable thinking mode for this request to prevent a 400 error from the API. + const hasIncompatibleHistory = historyHasToolCallsWithoutReasoning(convertedMessages) + const effectiveThinkingEnabled = isThinkingModel && !hasIncompatibleHistory + + if (hasIncompatibleHistory) { + logger.warn("provider_reasoning_guard_triggered", { + ctx: "deepseek", + provider: "deepseek", + modelId, + taskId: metadata?.taskId, + }) + } + + const thinking = supportsDeepSeekThinkingToggle(modelId) + ? ({ type: effectiveThinkingEnabled ? "enabled" : "disabled" } as const) + : effectiveThinkingEnabled + ? ({ type: "enabled" } as const) + : undefined + const deepSeekReasoningEffort = effectiveThinkingEnabled ? normalizeDeepSeekReasoningEffort(modelId, reasoningEffort) : undefined + const requestOptions: DeepSeekChatCompletionParams = { model: modelId, - ...(!isThinkingModel && { temperature: temperature ?? DEEP_SEEK_DEFAULT_TEMPERATURE }), + ...(!effectiveThinkingEnabled && { temperature: temperature ?? DEEP_SEEK_DEFAULT_TEMPERATURE }), messages: convertedMessages, stream: true as const, stream_options: { include_usage: true }, diff --git a/src/api/providers/mimo.ts b/src/api/providers/mimo.ts index 2901c2e926..caf4ccbf2f 100644 --- a/src/api/providers/mimo.ts +++ b/src/api/providers/mimo.ts @@ -14,6 +14,8 @@ import { extractReasoningFromDelta } from "./utils/extract-reasoning" import { OpenAiHandler } from "./openai" import type { ApiHandlerCreateMessageMetadata } from "../index" import { sanitizeOpenAiCallId } from "../../utils/tool-id" +import { historyHasToolCallsWithoutReasoning } from "./utils/reasoning-history-guard" +import { logger } from "../../utils/logging" /** * MiMoHandler extends OpenAiHandler with MiMo-specific adaptations. @@ -81,6 +83,20 @@ export class MimoHandler extends OpenAiHandler { const tools = metadata?.tools + // Layer 1 Guard: detect incompatible history (tool_calls without reasoning_content) + // and disable thinking mode for this request to prevent a 400 error from the API. + // MiMo previously had NO disable path — this closes that gap. + const hasIncompatibleHistory = historyHasToolCallsWithoutReasoning(convertedMessages) + + if (hasIncompatibleHistory) { + logger.warn("provider_reasoning_guard_triggered", { + ctx: "mimo", + provider: "mimo", + modelId, + taskId: metadata?.taskId, + }) + } + // Build request per MiMo's OpenAI-compatible API // https://developer.puter.com/ai/xiaomi/mimo-v2.5-pro/ // Note: temperature is omitted because MiMo forces it to 1.0 when thinking mode @@ -91,7 +107,7 @@ export class MimoHandler extends OpenAiHandler { stream: true, stream_options: { include_usage: true }, // MiMo requires thinking to be enabled via extra_body - extra_body: { thinking: { type: "enabled" } }, + extra_body: { thinking: { type: hasIncompatibleHistory ? "disabled" : "enabled" } }, } if (tools && tools.length > 0) { diff --git a/src/api/providers/utils/__tests__/reasoning-history-guard.spec.ts b/src/api/providers/utils/__tests__/reasoning-history-guard.spec.ts new file mode 100644 index 0000000000..3601bd7c41 --- /dev/null +++ b/src/api/providers/utils/__tests__/reasoning-history-guard.spec.ts @@ -0,0 +1,107 @@ +// npx vitest run api/providers/utils/__tests__/reasoning-history-guard.spec.ts + +import { historyHasToolCallsWithoutReasoning } from "../reasoning-history-guard" + +describe("historyHasToolCallsWithoutReasoning", () => { + it("returns false for empty messages", () => { + expect(historyHasToolCallsWithoutReasoning([])).toBe(false) + }) + + it("returns false when no assistant messages have tool_calls", () => { + const messages = [ + { role: "user", content: "hello" }, + { role: "assistant", content: "hi there" }, + ] + expect(historyHasToolCallsWithoutReasoning(messages)).toBe(false) + }) + + it("returns false when assistant messages have tool_calls with reasoning_content", () => { + const messages = [ + { + role: "assistant", + content: null, + tool_calls: [{ id: "call_1", function: { name: "test" } }], + reasoning_content: "I should call test because...", + }, + ] + expect(historyHasToolCallsWithoutReasoning(messages)).toBe(false) + }) + + it("returns true when assistant messages have tool_calls but no reasoning_content field", () => { + const messages = [ + { + role: "assistant", + content: null, + tool_calls: [{ id: "call_1", function: { name: "test" } }], + }, + ] + expect(historyHasToolCallsWithoutReasoning(messages)).toBe(true) + }) + + it("returns true when assistant messages have tool_calls with empty reasoning_content", () => { + const messages = [ + { + role: "assistant", + content: null, + tool_calls: [{ id: "call_1", function: { name: "test" } }], + reasoning_content: "", + }, + ] + expect(historyHasToolCallsWithoutReasoning(messages)).toBe(true) + }) + + it("returns true when assistant messages have empty tool_calls array", () => { + const messages = [ + { + role: "assistant", + content: "hello", + tool_calls: [], + }, + ] + expect(historyHasToolCallsWithoutReasoning(messages)).toBe(false) + }) + + it("returns false for non-assistant messages with tool_calls", () => { + const messages = [ + { + role: "user", + content: "hello", + tool_calls: [{ id: "call_1" }], + }, + ] + expect(historyHasToolCallsWithoutReasoning(messages)).toBe(false) + }) + + it("returns true when at least one assistant message is missing reasoning_content despite tool_calls", () => { + const messages = [ + { + role: "assistant", + content: "Let me think...", + reasoning_content: "thinking step 1", + }, + { + role: "assistant", + content: null, + tool_calls: [{ id: "call_1", function: { name: "read_file" } }], + // no reasoning_content — this is the problematic message + }, + { + role: "tool", + content: "file content", + tool_call_id: "call_1", + }, + ] + expect(historyHasToolCallsWithoutReasoning(messages)).toBe(true) + }) + + it("handles non-array tool_calls gracefully", () => { + const messages = [ + { + role: "assistant", + content: null, + tool_calls: "not-an-array" as unknown as unknown[], + }, + ] + expect(historyHasToolCallsWithoutReasoning(messages)).toBe(false) + }) +}) diff --git a/src/api/providers/utils/reasoning-history-guard.ts b/src/api/providers/utils/reasoning-history-guard.ts new file mode 100644 index 0000000000..e44f3a1081 --- /dev/null +++ b/src/api/providers/utils/reasoning-history-guard.ts @@ -0,0 +1,26 @@ +/** + * Detects whether a converted OpenAI-format message history contains any + * assistant message with `tool_calls` but no non-empty `reasoning_content`. + * Used as a guard before enabling strict provider "thinking" modes that + * require reasoning_content to accompany every tool-call turn. + * + * This function operates on messages *after* conversion to the provider's + * OpenAI-compatible format (e.g. `convertToR1Format`, `convertToZAiFormat`), + * because it is only in the converted format that `reasoning_content` + * presence can be reliably determined. + * + * @param messages - Array of converted messages in OpenAI-compatible format + * @returns `true` if any assistant message has tool_calls but lacks + * non-empty reasoning_content + */ +export function historyHasToolCallsWithoutReasoning( + messages: Array<{ role?: string; tool_calls?: unknown[]; reasoning_content?: unknown }>, +): boolean { + return messages.some( + (m) => + m.role === "assistant" && + Array.isArray(m.tool_calls) && + m.tool_calls.length > 0 && + (typeof m.reasoning_content !== "string" || m.reasoning_content.length === 0), + ) +} diff --git a/src/api/providers/zai.ts b/src/api/providers/zai.ts index 4854c814fd..910557fd1d 100644 --- a/src/api/providers/zai.ts +++ b/src/api/providers/zai.ts @@ -13,10 +13,12 @@ import { import { type ApiHandlerOptions, getModelMaxOutputTokens } from "../../shared/api" import { convertToZAiFormat } from "../transform/zai-format" +import { historyHasToolCallsWithoutReasoning } from "./utils/reasoning-history-guard" import type { ApiHandlerCreateMessageMetadata } from "../index" import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider" import { handleOpenAIError } from "./utils/error-handler" +import { logger } from "../../utils/logging" // Custom interface for Z.ai params to support thinking mode and reasoning effort tiers. // Z.ai accepts the standard `reasoning_effort` ladder (none/minimal/low/medium/high/xhigh/max) @@ -107,6 +109,22 @@ export class ZAiHandler extends BaseOpenAiCompatibleProvider { // Use Z.ai format to preserve reasoning_content and merge post-tool text into tool messages const convertedMessages = convertToZAiFormat(messages, { mergeToolResultText: true }) + // Layer 1 Guard: detect incompatible history (tool_calls without reasoning_content) + // and disable thinking mode for this request to prevent a 400 error from the API. + const hasIncompatibleHistory = historyHasToolCallsWithoutReasoning(convertedMessages) + + if (hasIncompatibleHistory) { + logger.warn("provider_reasoning_guard_triggered", { + ctx: "zai", + provider: "zai", + model, + taskId: metadata?.taskId, + }) + } + + // When incompatible history is detected, force reasoning off regardless of user settings. + const effectiveReasoning = hasIncompatibleHistory ? false : useReasoning + const params: ZAiChatCompletionParams = { model, max_tokens, @@ -115,8 +133,8 @@ export class ZAiHandler extends BaseOpenAiCompatibleProvider { stream: true, stream_options: { include_usage: true }, // Thinking is ON by default for these models, so explicitly disable it when needed. - thinking: useReasoning ? { type: "enabled" } : { type: "disabled" }, - reasoning_effort: reasoningEffort, + thinking: effectiveReasoning ? { type: "enabled" } : { type: "disabled" }, + reasoning_effort: effectiveReasoning ? reasoningEffort : undefined, tools: this.convertToolsForOpenAI(metadata?.tools), tool_choice: metadata?.tool_choice, parallel_tool_calls: metadata?.parallelToolCalls ?? true, diff --git a/src/core/tools/SwitchModeTool.ts b/src/core/tools/SwitchModeTool.ts index a60ce63bde..ebeeda45e6 100644 --- a/src/core/tools/SwitchModeTool.ts +++ b/src/core/tools/SwitchModeTool.ts @@ -56,7 +56,8 @@ export class SwitchModeTool extends BaseTool<"switch_mode"> { } // Switch the mode using shared handler - await task.providerRef.deref()?.handleModeSwitch(mode_slug) + // via: "switch_mode" — explicit tool call + await task.providerRef.deref()?.handleModeSwitch(mode_slug, "switch_mode") pushToolResult( `Successfully switched from ${getModeBySlug(currentMode)?.name ?? currentMode} mode to ${ diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 912fed7837..bbefdee590 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -98,6 +98,8 @@ import { t } from "../../i18n" import { buildApiHandler } from "../../api" import { forceFullModelDetailsLoad, hasLoadedFullDetails } from "../../api/providers/fetchers/lmstudio" +import { logger } from "../../utils/logging" +import { modeSwitchRisksReasoningIncompatibility } from "../../shared/reasoning-mode-compatibility" import { ContextProxy } from "../config/ContextProxy" import { ProviderSettingsManager } from "../config/ProviderSettingsManager" import { CustomModesManager } from "../config/CustomModesManager" @@ -1506,14 +1508,68 @@ export class ClineProvider /** * Handle switching to a new mode, including updating the associated API configuration * @param newMode The mode to switch to + * @param via - The origin of the mode switch: + * "switch_mode" — explicit switch_mode tool call + * "new_task_delegation" — delegation via new_task + * "unknown_bypass" — cannot be attributed to explicit tool call (default) */ - public async handleModeSwitch(newMode: Mode) { + public async handleModeSwitch(newMode: Mode, via: "switch_mode" | "new_task_delegation" | "unknown_bypass" = "unknown_bypass") { const task = this.getCurrentTask() + const fromMode = (await this.getState())?.mode ?? "unknown" if (task) { TelemetryService.instance.captureModeSwitch(task.taskId, newMode) task.emit(RooCodeEventName.TaskModeSwitched, task.taskId, newMode) + // Layer 2 — Orchestration safety: check for reasoning-mode incompatibility + // when switching modes without proper delegation (only relevant for bypass switches). + if (via !== "new_task_delegation") { + try { + const fromProviderName = task.apiConfiguration?.apiProvider + const toConfigId = await this.providerSettingsManager.getModeConfigId(newMode) + const listApiConfig = await this.providerSettingsManager.listConfig() + const toProfile = toConfigId + ? listApiConfig.find((c) => c.id === toConfigId) + : undefined + const toProviderName = toProfile + ? (await this.providerSettingsManager.getProfile({ name: toProfile.name })).apiProvider + : fromProviderName + + if ( + toProviderName && + modeSwitchRisksReasoningIncompatibility(fromProviderName, toProviderName) + ) { + // Behavior A — always show visible warning in chat + await task.say( + "mode_switch_compatibility_warning", + JSON.stringify({ + fromMode, + toMode: newMode, + fromProvider: fromProviderName, + toProvider: toProviderName, + via, + }), + undefined, + undefined, + undefined, + undefined, + { isNonInteractive: true }, + ) + + // Behavior B — optional auto-condense when setting is enabled + const autoCondense = this.context.workspaceState.get("autoCondenseOnRiskyModeSwitch", false) + if (autoCondense) { + await task.condenseContext() + } + } + } catch (innerError) { + // Non-fatal: log but don't block the mode switch if the check fails. + this.log( + `Mode-switch compatibility check failed: ${innerError instanceof Error ? innerError.message : String(innerError)}`, + ) + } + } + try { // Update the task history with the new mode first. const taskHistoryItem = diff --git a/src/shared/__tests__/reasoning-mode-compatibility.spec.ts b/src/shared/__tests__/reasoning-mode-compatibility.spec.ts new file mode 100644 index 0000000000..cea46cbd9f --- /dev/null +++ b/src/shared/__tests__/reasoning-mode-compatibility.spec.ts @@ -0,0 +1,71 @@ +// npx vitest run shared/__tests__/reasoning-mode-compatibility.spec.ts + +import { isStrictReasoningModeProvider, modeSwitchRisksReasoningIncompatibility } from "../reasoning-mode-compatibility" + +describe("isStrictReasoningModeProvider", () => { + it("returns true for deepseek", () => { + expect(isStrictReasoningModeProvider("deepseek")).toBe(true) + }) + + it("returns true for zai", () => { + expect(isStrictReasoningModeProvider("zai")).toBe(true) + }) + + it("returns true for mimo", () => { + expect(isStrictReasoningModeProvider("mimo")).toBe(true) + }) + + it("returns false for anthropic", () => { + expect(isStrictReasoningModeProvider("anthropic")).toBe(false) + }) + + it("returns false for undefined", () => { + expect(isStrictReasoningModeProvider(undefined)).toBe(false) + }) + + it("returns false for a non-strict provider", () => { + expect(isStrictReasoningModeProvider("openai-native")).toBe(false) + }) + + it("returns false for gemini", () => { + expect(isStrictReasoningModeProvider("gemini")).toBe(false) + }) +}) + +describe("modeSwitchRisksReasoningIncompatibility", () => { + it("returns false when from and to are the same provider", () => { + expect(modeSwitchRisksReasoningIncompatibility("deepseek", "deepseek")).toBe(false) + expect(modeSwitchRisksReasoningIncompatibility("anthropic", "anthropic")).toBe(false) + expect(modeSwitchRisksReasoningIncompatibility("zai", "zai")).toBe(false) + }) + + it("returns true when switching from non-strict to strict provider", () => { + expect(modeSwitchRisksReasoningIncompatibility("anthropic", "deepseek")).toBe(true) + expect(modeSwitchRisksReasoningIncompatibility("openai-native", "zai")).toBe(true) + expect(modeSwitchRisksReasoningIncompatibility("gemini", "mimo")).toBe(true) + }) + + it("returns false when switching from strict to non-strict provider", () => { + expect(modeSwitchRisksReasoningIncompatibility("deepseek", "anthropic")).toBe(false) + expect(modeSwitchRisksReasoningIncompatibility("zai", "openai-native")).toBe(false) + expect(modeSwitchRisksReasoningIncompatibility("mimo", "gemini")).toBe(false) + }) + + it("returns false when switching between two strict providers", () => { + expect(modeSwitchRisksReasoningIncompatibility("deepseek", "zai")).toBe(false) + expect(modeSwitchRisksReasoningIncompatibility("zai", "mimo")).toBe(false) + expect(modeSwitchRisksReasoningIncompatibility("mimo", "deepseek")).toBe(false) + }) + + it("returns true when going from undefined (unknown) provider to a strict provider (fail-safe)", () => { + expect(modeSwitchRisksReasoningIncompatibility(undefined, "deepseek")).toBe(true) + }) + + it("returns false when going from strict to undefined", () => { + expect(modeSwitchRisksReasoningIncompatibility("deepseek", undefined)).toBe(false) + }) + + it("returns false when both are undefined", () => { + expect(modeSwitchRisksReasoningIncompatibility(undefined, undefined)).toBe(false) + }) +}) diff --git a/src/shared/reasoning-mode-compatibility.ts b/src/shared/reasoning-mode-compatibility.ts new file mode 100644 index 0000000000..8b8ec11b13 --- /dev/null +++ b/src/shared/reasoning-mode-compatibility.ts @@ -0,0 +1,37 @@ +/** + * Providers/models known to require reasoning_content on every tool-call + * turn once their "thinking" mode is active. Mirrors preserveReasoning + * in @roo-code/types model definitions — kept here as an explicit allowlist + * so orchestration-level checks don't need to resolve full ModelInfo. + * + * Must be kept in sync with providers that have at least one model with + * `preserveReasoning: true` and are covered by the Layer 1 guard in + * `src/api/providers/utils/reasoning-history-guard.ts`. + */ +const STRICT_REASONING_MODE_PROVIDERS = new Set(["deepseek", "zai", "mimo"]) + +/** + * Returns `true` if the given provider name is one of the known strict- + * reasoning providers that enforce `reasoning_content` on tool-call turns. + */ +export function isStrictReasoningModeProvider(provider: string | undefined): boolean { + return !!provider && STRICT_REASONING_MODE_PROVIDERS.has(provider) +} + +/** + * True when switching from `fromProvider` to `toProvider` risks carrying + * over a conversation history that is incompatible with the target + * provider's strict reasoning-mode formatting requirements — i.e. the + * target enforces reasoning_content on tool-call turns but the source + * provider's history was never built with that field. + * + * Returns `false` when switching within the same provider family (no + * format mismatch) or when neither provider is strict. + */ +export function modeSwitchRisksReasoningIncompatibility( + fromProvider: string | undefined, + toProvider: string | undefined, +): boolean { + if (fromProvider === toProvider) return false + return isStrictReasoningModeProvider(toProvider) && !isStrictReasoningModeProvider(fromProvider) +} diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index f973c7929f..256a158b42 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -1577,6 +1577,26 @@ export const ChatRowContent = ({ /> ) } + case "mode_switch_compatibility_warning": { + const warningData = safeJsonParse<{ + fromMode?: string + toMode?: string + fromProvider?: string + toProvider?: string + via?: string + }>(message.text || "{}") + if (!warningData) return null + return ( + + ) + } default: return ( <> diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index b61063797b..5f54419477 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -367,6 +367,10 @@ } }, "profileViolationWarning": "El perfil actual no és compatible amb la configuració de la teva organització", + "modeSwitchCompatibilityWarning": { + "title": "Canvi de mode sense delegació formal", + "message": "⚠️ S'ha canviat del mode {{fromMode}} al mode {{toMode}} sense delegació de tasca — l'historial de conversa pot ser incompatible amb el nou model ({{toProvider}})." + }, "shellIntegration": { "title": "Advertència d'execució d'ordres", "description": "La teva ordre s'està executant sense la integració de shell del terminal VSCode. Per suprimir aquest advertiment, pots desactivar la integració de shell a la secció Terminal de la configuració de Zoo Code o solucionar problemes d'integració del terminal VSCode utilitzant l'enllaç de sota.", diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index 30e584fcfe..1648905b78 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -367,6 +367,10 @@ } }, "profileViolationWarning": "Das aktuelle Profil ist nicht kompatibel mit den Einstellungen deiner Organisation", + "modeSwitchCompatibilityWarning": { + "title": "Moduswechsel ohne formale Delegation", + "message": "⚠️ Modus von {{fromMode}} zu {{toMode}} ohne Aufgaben-Delegation gewechselt — der Gesprächsverlauf ist möglicherweise mit dem neuen Modell ({{toProvider}}) inkompatibel." + }, "shellIntegration": { "title": "Befehlsausführungswarnung", "description": "Dein Befehl wird ohne VSCode Terminal-Shell-Integration ausgeführt. Um diese Warnung zu unterdrücken, kannst du die Shell-Integration im Abschnitt Terminal der Zoo Code Einstellungen deaktivieren oder die VSCode Terminal-Integration mit dem Link unten beheben.", diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index 0a4306e3b2..4ef4679a97 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -394,6 +394,10 @@ } }, "profileViolationWarning": "The current profile isn't compatible with your organization's settings", + "modeSwitchCompatibilityWarning": { + "title": "Mode switch without formal delegation", + "message": "\u26a0\ufe0f Mode switched from {{fromMode}} to {{toMode}} without task delegation \u2014 conversation history may be incompatible with the new model ({{toProvider}})." + }, "shellIntegration": { "title": "Command Execution Warning", "description": "Your command is being executed without VSCode terminal shell integration. To suppress this warning you can disable shell integration in the Terminal section of the Zoo Code settings or troubleshoot VSCode terminal integration using the link below.", diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index b6f8a70c1c..b87aeca7b7 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -367,6 +367,10 @@ } }, "profileViolationWarning": "El perfil actual no es compatible con la configuración de tu organización", + "modeSwitchCompatibilityWarning": { + "title": "Cambio de modo sin delegación formal", + "message": "⚠️ Se cambió del modo {{fromMode}} al modo {{toMode}} sin delegación de tarea — el historial de conversación puede ser incompatible con el nuevo modelo ({{toProvider}})." + }, "shellIntegration": { "title": "Advertencia de ejecución de comandos", "description": "Tu comando se está ejecutando sin la integración de shell del terminal de VSCode. Para ocultar esta advertencia, puedes desactivar la integración de shell en la sección Terminal de la configuración de Zoo Code o solucionar los problemas de integración del terminal de VSCode con el enlace de abajo.", diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index 72d0676bd7..8ad38c87ca 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -367,6 +367,10 @@ } }, "profileViolationWarning": "Le profil actuel n'est pas compatible avec les paramètres de votre organisation", + "modeSwitchCompatibilityWarning": { + "title": "Changement de mode sans délégation formelle", + "message": "⚠️ Mode passé de {{fromMode}} à {{toMode}} sans délégation de tâche — l'historique de conversation peut être incompatible avec le nouveau modèle ({{toProvider}})." + }, "shellIntegration": { "title": "Avertissement d'exécution de commande", "description": "Ta commande est exécutée sans l'intégration shell du terminal VSCode. Pour masquer cet avertissement, tu peux désactiver l'intégration shell dans la section Terminal des paramètres de Zoo Code ou résoudre les problèmes d'intégration du terminal VSCode avec le lien ci-dessous.", diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index f022d0b35c..8b0708069d 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -367,6 +367,10 @@ } }, "profileViolationWarning": "वर्तमान प्रोफ़ाइल आपके संगठन की सेटिंग्स के साथ संगत नहीं है", + "modeSwitchCompatibilityWarning": { + "title": "औपचारिक प्रत्यायोजन के बिना मोड स्विच", + "message": "⚠️ कार्य प्रत्यायोजन के बिना मोड {{fromMode}} से {{toMode}} में बदला गया — वार्तालाप इतिहास नए मॉडल ({{toProvider}}) के साथ असंगत हो सकता है।" + }, "shellIntegration": { "title": "कमांड निष्पादन चेतावनी", "description": "आपका कमांड VSCode टर्मिनल शेल इंटीग्रेशन के बिना निष्पादित हो रहा है। इस चेतावनी को दबाने के लिए आप Zoo Code सेटिंग्स के Terminal अनुभाग में शेल इंटीग्रेशन को अक्षम कर सकते हैं या नीचे दिए गए लिंक का उपयोग करके VSCode टर्मिनल इंटीग्रेशन की समस्या का समाधान कर सकते हैं।", diff --git a/webview-ui/src/i18n/locales/id/chat.json b/webview-ui/src/i18n/locales/id/chat.json index 3f719e101c..d9faa61360 100644 --- a/webview-ui/src/i18n/locales/id/chat.json +++ b/webview-ui/src/i18n/locales/id/chat.json @@ -404,6 +404,10 @@ } }, "profileViolationWarning": "Profil saat ini tidak kompatibel dengan pengaturan organisasi kamu", + "modeSwitchCompatibilityWarning": { + "title": "Perubahan mode tanpa delegasi formal", + "message": "⚠️ Mode berpindah dari {{fromMode}} ke {{toMode}} tanpa delegasi tugas — riwayat percakapan mungkin tidak kompatibel dengan model baru ({{toProvider}})." + }, "shellIntegration": { "title": "Peringatan Eksekusi Perintah", "description": "Perintah kamu dijalankan tanpa integrasi shell terminal VSCode. Untuk menekan peringatan ini kamu bisa menonaktifkan integrasi shell di bagian Terminal dari pengaturan Zoo Code atau troubleshoot integrasi terminal VSCode menggunakan link di bawah.", diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index a172a9275c..9e618f33ec 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -367,6 +367,10 @@ } }, "profileViolationWarning": "Il profilo corrente non è compatibile con le impostazioni della tua organizzazione", + "modeSwitchCompatibilityWarning": { + "title": "Cambio modalità senza delega formale", + "message": "⚠️ Modalità passata da {{fromMode}} a {{toMode}} senza delega di attività — la cronologia della conversazione potrebbe essere incompatibile con il nuovo modello ({{toProvider}})." + }, "shellIntegration": { "title": "Avviso di esecuzione comando", "description": "Il tuo comando viene eseguito senza l'integrazione shell del terminale VSCode. Per sopprimere questo avviso puoi disattivare l'integrazione shell nella sezione Terminal delle impostazioni di Zoo Code o risolvere i problemi di integrazione del terminale VSCode utilizzando il link qui sotto.", diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index a51f6d9fa0..83e351161e 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -367,6 +367,10 @@ } }, "profileViolationWarning": "現在のプロファイルは組織の設定と互換性がありません", + "modeSwitchCompatibilityWarning": { + "title": "正式な委任なしのモード切り替え", + "message": "⚠️ タスク委任なしでモードが {{fromMode}} から {{toMode}} に切り替わりました — 会話履歴が新しいモデル ({{toProvider}}) と互換性がない可能性があります。" + }, "shellIntegration": { "title": "コマンド実行警告", "description": "コマンドはVSCodeターミナルシェル統合なしで実行されています。この警告を非表示にするには、Zoo Code 設定Terminalセクションでシェル統合を無効にするか、以下のリンクを使用してVSCodeターミナル統合のトラブルシューティングを行ってください。", diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index cdfecf3a10..aa249a9cba 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -367,6 +367,10 @@ } }, "profileViolationWarning": "현재 프로필이 조직 설정과 호환되지 않습니다", + "modeSwitchCompatibilityWarning": { + "title": "공식 위임 없는 모드 전환", + "message": "⚠️ 작업 위임 없이 모드가 {{fromMode}}에서 {{toMode}}로 전환되었습니다 — 대화 기록이 새 모델({{toProvider}})과 호환되지 않을 수 있습니다." + }, "shellIntegration": { "title": "명령 실행 경고", "description": "명령이 VSCode 터미널 쉘 통합 없이 실행되고 있습니다. 이 경고를 숨기려면 Zoo Code 설정Terminal 섹션에서 쉘 통합을 비활성화하거나 아래 링크를 사용하여 VSCode 터미널 통합 문제를 해결하세요.", diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index 06d3c483ed..d1c9b2da1b 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -367,6 +367,10 @@ } }, "profileViolationWarning": "Het huidige profiel is niet compatibel met de instellingen van uw organisatie", + "modeSwitchCompatibilityWarning": { + "title": "Moduswissel zonder formele delegatie", + "message": "⚠️ Modus gewijzigd van {{fromMode}} naar {{toMode}} zonder taakdelegatie — de gespreksgeschiedenis is mogelijk niet compatibel met het nieuwe model ({{toProvider}})." + }, "shellIntegration": { "title": "Waarschuwing commando-uitvoering", "description": "Je commando wordt uitgevoerd zonder VSCode-terminal-shellintegratie. Om deze waarschuwing te onderdrukken kun je shellintegratie uitschakelen in de sectie Terminal van de Zoo Code-instellingen of de VSCode-terminalintegratie oplossen via de onderstaande link.", diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index d60691ca53..a9e24b57a2 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -367,6 +367,10 @@ } }, "profileViolationWarning": "Bieżący profil nie jest kompatybilny z ustawieniami Twojej organizacji", + "modeSwitchCompatibilityWarning": { + "title": "Zmiana trybu bez formalnej delegacji", + "message": "⚠️ Tryb zmieniony z {{fromMode}} na {{toMode}} bez delegacji zadania — historia rozmowy może być niekompatybilna z nowym modelem ({{toProvider}})." + }, "shellIntegration": { "title": "Ostrzeżenie wykonania polecenia", "description": "Twoje polecenie jest wykonywane bez integracji powłoki terminala VSCode. Aby ukryć to ostrzeżenie, możesz wyłączyć integrację powłoki w sekcji Terminal w ustawieniach Zoo Code lub rozwiązać problemy z integracją terminala VSCode korzystając z poniższego linku.", diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index cf71819ebe..30cb19c70e 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -367,6 +367,10 @@ } }, "profileViolationWarning": "O perfil atual não é compatível com as configurações da sua organização", + "modeSwitchCompatibilityWarning": { + "title": "Mudança de modo sem delegação formal", + "message": "⚠️ Modo alterado de {{fromMode}} para {{toMode}} sem delegação de tarefa — o histórico da conversa pode ser incompatível com o novo modelo ({{toProvider}})." + }, "shellIntegration": { "title": "Aviso de execução de comando", "description": "Seu comando está sendo executado sem a integração de shell do terminal VSCode. Para suprimir este aviso, você pode desativar a integração de shell na seção Terminal das configurações do Zoo Code ou solucionar problemas de integração do terminal VSCode usando o link abaixo.", diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index 3a3c36b038..082beae0b3 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -368,6 +368,10 @@ } }, "profileViolationWarning": "Текущий профиль несовместим с настройками вашей организации", + "modeSwitchCompatibilityWarning": { + "title": "Переключение режима без формального делегирования", + "message": "⚠️ Режим переключён с {{fromMode}} на {{toMode}} без делегирования задачи — история разговора может быть несовместима с новой моделью ({{toProvider}})." + }, "shellIntegration": { "title": "Предупреждение о выполнении команды", "description": "Ваша команда выполняется без интеграции оболочки терминала VSCode. Чтобы скрыть это предупреждение, вы можете отключить интеграцию оболочки в разделе Terminal в настройках Zoo Code или устранить проблемы с интеграцией терминала VSCode, используя ссылку ниже.", diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index ad4b9424b8..32b8a91407 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -368,6 +368,10 @@ } }, "profileViolationWarning": "Geçerli profil kuruluşunuzun ayarlarıyla uyumlu değil", + "modeSwitchCompatibilityWarning": { + "title": "Resmi yetki devri olmadan mod değişimi", + "message": "⚠️ Görev yetki devri olmadan mod {{fromMode}}'dan {{toMode}}'a değiştirildi — konuşma geçmişi yeni modelle ({{toProvider}}) uyumlu olmayabilir." + }, "shellIntegration": { "title": "Komut Çalıştırma Uyarısı", "description": "Komutunuz VSCode terminal kabuk entegrasyonu olmadan çalıştırılıyor. Bu uyarıyı gizlemek için Zoo Code ayarları'nın Terminal bölümünden kabuk entegrasyonunu devre dışı bırakabilir veya aşağıdaki bağlantıyı kullanarak VSCode terminal entegrasyonu sorunlarını giderebilirsiniz.", diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index 1b21342692..da2163d846 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -368,6 +368,10 @@ } }, "profileViolationWarning": "Hồ sơ hiện tại không tương thích với cài đặt của tổ chức của bạn", + "modeSwitchCompatibilityWarning": { + "title": "Chuyển chế độ mà không có ủy quyền chính thức", + "message": "⚠️ Chế độ đã chuyển từ {{fromMode}} sang {{toMode}} mà không ủy quyền nhiệm vụ — lịch sử hội thoại có thể không tương thích với mô hình mới ({{toProvider}})." + }, "shellIntegration": { "title": "Cảnh báo thực thi lệnh", "description": "Lệnh của bạn đang được thực thi mà không có tích hợp shell terminal VSCode. Để ẩn cảnh báo này, bạn có thể tắt tích hợp shell trong phần Terminal của cài đặt Zoo Code hoặc khắc phục sự cố tích hợp terminal VSCode bằng liên kết bên dưới.", diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index d264934150..9a48602905 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -368,6 +368,10 @@ } }, "profileViolationWarning": "当前配置文件与您的组织设置不兼容", + "modeSwitchCompatibilityWarning": { + "title": "未经正式委派切换模式", + "message": "⚠️ 模式已从 {{fromMode}} 切换到 {{toMode}},未进行任务委派 — 对话历史可能与新模型 ({{toProvider}}) 不兼容。" + }, "shellIntegration": { "title": "命令执行警告", "description": "您的命令正在没有 VSCode 终端 shell 集成的情况下执行。要隐藏此警告,您可以在 Zoo Code 设置Terminal 部分禁用 shell 集成,或使用下方链接排查 VSCode 终端集成问题。", diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index 722403a78b..f718d6933d 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -397,6 +397,10 @@ } }, "profileViolationWarning": "目前設定檔與您的組織設定不相容", + "modeSwitchCompatibilityWarning": { + "title": "未經正式委派切換模式", + "message": "⚠️ 模式已從 {{fromMode}} 切換到 {{toMode}},未進行任務委派 — 對話歷史可能與新模型 ({{toProvider}}) 不相容。" + }, "shellIntegration": { "title": "命令執行警告", "description": "命令正在沒有 VS Code 終端機 Shell 整合的情況下執行。若要隱藏此警告,可在 Zoo Code 設定終端機 區塊中停用 Shell 整合,或使用下方連結排解 VS Code 終端機整合問題。",