diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index f1c4b81c48..fd94efb8e7 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -205,6 +205,7 @@ export const SECRET_STATE_KEYS = [ "fireworksApiKey", "featherlessApiKey", "ioIntelligenceApiKey", + "morphApiKey", "vercelAiGatewayApiKey", ] as const diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 4dfeacbf07..6109008333 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -101,6 +101,10 @@ const baseProviderSettingsSchema = z.object({ diffEnabled: z.boolean().optional(), todoListEnabled: z.boolean().optional(), fuzzyMatchThreshold: z.number().optional(), + // Morph Fast Apply - optional provider-scoped controls + morphFastApplyEnabled: z.boolean().optional(), + morphApiKey: z.string().optional(), + modelTemperature: z.number().nullish(), rateLimitSeconds: z.number().optional(), consecutiveMistakeLimit: z.number().min(0).optional(), diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index c5be865731..7bd6c4a3f2 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -394,17 +394,18 @@ export class Task extends EventEmitter implements TaskLike { // Only set up diff strategy if diff is enabled. if (this.diffEnabled) { - // Default to old strategy, will be updated if experiment is enabled. + // Default to legacy single-file strategy this.diffStrategy = new MultiSearchReplaceDiffStrategy(this.fuzzyMatchThreshold) - // Check experiment asynchronously and update strategy if needed. + // Prefer MultiFile when Morph Fast Apply is enabled, otherwise respect experiment flag. provider.getState().then((state) => { const isMultiFileApplyDiffEnabled = experiments.isEnabled( state.experiments ?? {}, EXPERIMENT_IDS.MULTI_FILE_APPLY_DIFF, ) + const isMorphFastApplyEnabled = state.apiConfiguration?.morphFastApplyEnabled === true - if (isMultiFileApplyDiffEnabled) { + if (isMorphFastApplyEnabled || isMultiFileApplyDiffEnabled) { this.diffStrategy = new MultiFileSearchReplaceDiffStrategy(this.fuzzyMatchThreshold) } }) diff --git a/src/core/webview/generateSystemPrompt.ts b/src/core/webview/generateSystemPrompt.ts index a639d8a960..c0142bbf9a 100644 --- a/src/core/webview/generateSystemPrompt.ts +++ b/src/core/webview/generateSystemPrompt.ts @@ -27,15 +27,20 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web maxConcurrentFileReads, } = await provider.getState() - // Check experiment to determine which diff strategy to use + // Determine which diff strategy to use: + // - If Morph Fast Apply is enabled, force MultiFile strategy for best tolerance and batching + // - Otherwise, fall back to experiment flag to choose between MultiFile and single-file strategies const isMultiFileApplyDiffEnabled = experimentsModule.isEnabled( experiments ?? {}, EXPERIMENT_IDS.MULTI_FILE_APPLY_DIFF, ) + const isMorphFastApplyEnabled = apiConfiguration?.morphFastApplyEnabled === true - const diffStrategy = isMultiFileApplyDiffEnabled + const diffStrategy = isMorphFastApplyEnabled ? new MultiFileSearchReplaceDiffStrategy(fuzzyMatchThreshold) - : new MultiSearchReplaceDiffStrategy(fuzzyMatchThreshold) + : isMultiFileApplyDiffEnabled + ? new MultiFileSearchReplaceDiffStrategy(fuzzyMatchThreshold) + : new MultiSearchReplaceDiffStrategy(fuzzyMatchThreshold) const cwd = provider.cwd diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 7f2ac4ed7a..f7872589bf 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -1,7 +1,7 @@ import React, { memo, useCallback, useEffect, useMemo, useState } from "react" import { convertHeadersToObject } from "./utils/headers" import { useDebounce } from "react-use" -import { VSCodeLink, VSCodeButton } from "@vscode/webview-ui-toolkit/react" +import { VSCodeLink, VSCodeButton, VSCodeCheckbox, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import { ExternalLinkIcon } from "@radix-ui/react-icons" import { @@ -765,6 +765,32 @@ const ApiOptions = ({ fuzzyMatchThreshold={apiConfiguration.fuzzyMatchThreshold} onChange={(field, value) => setApiConfigurationField(field, value)} /> + + {/* Morph Fast Apply - provider-scoped controls */} +
+ +
+ + setApiConfigurationField("morphFastApplyEnabled", e.target.checked) + }> + Enable Morph Fast Apply (fuzzy multi-file) + + + setApiConfigurationField("morphApiKey", e.target.value)} + placeholder="Optional API key"> + Morph API key (optional) + +
+ When enabled, apply_diff uses faster tolerant matching across multiple files. An API + key is only required if using a hosted Morph service. +
+
+
{selectedModelInfo?.supportsTemperature !== false && (