Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ export const globalSettingsSchema = z.object({
// kilocode_change start: Morph fast apply
morphApiKey: z.string().optional(),
fastApplyModel: fastApplyModelSchema.optional(),
fastApplyProviderType: z.enum(["morph", "openrouter", "kilocode"]).optional(),
fastApplyProfileId: z.string().optional(),
// kilocode_change end

codebaseIndexModels: codebaseIndexModelsSchema.optional(),
Expand Down
6 changes: 3 additions & 3 deletions src/core/prompts/sections/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CodeIndexManager } from "../../../services/code-index/manager"
// kilocode_change start
import { getFastApplyEditingInstructions } from "../tools/edit-file"
import { type ClineProviderState } from "../../webview/ClineProvider"
import { getFastApplyModelType, isFastApplyAvailable } from "../../tools/editFileTool"
import { isFastApplyAvailable } from "../../tools/editFileTool"
// kilocode_change end

function getEditingInstructions(diffStrategy?: DiffStrategy): string {
Expand Down Expand Up @@ -68,7 +68,7 @@ export function getRulesSection(
? "- **CRITICAL: For ANY exploration of code you haven't examined yet in this conversation, you MUST use the `codebase_search` tool FIRST before using search_files or other file exploration tools.** This requirement applies throughout the entire conversation, not just when starting a task. The codebase_search tool uses semantic search to find relevant code based on meaning, not just keywords, making it much more effective for understanding how features are implemented. Even if you've already explored some parts of the codebase, any new area or functionality you need to understand requires using codebase_search first.\n"
: ""

const kiloCodeUseMorph = isFastApplyAvailable(clineProviderState)
const kiloCodeUseMorph = isFastApplyAvailable()

return `====

Expand All @@ -81,7 +81,7 @@ RULES
- Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '${cwd.toPosix()}', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '${cwd.toPosix()}'). For example, if you needed to run \`npm install\` in a project outside of '${cwd.toPosix()}', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`.
${codebaseSearchRule}- When using the search_files tool${isCodebaseSearchAvailable ? " (after codebase_search)" : ""}, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using ${kiloCodeUseMorph ? "edit_file" : diffStrategy ? "apply_diff or write_to_file" : "write_to_file"} to make informed changes.
- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the ${kiloCodeUseMorph ? "edit_file" : "write_to_file"} tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.
${kiloCodeUseMorph ? getFastApplyEditingInstructions(getFastApplyModelType(clineProviderState)) : getEditingInstructions(diffStrategy)}
${kiloCodeUseMorph ? getFastApplyEditingInstructions() : getEditingInstructions(diffStrategy)}
- Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode.
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
* For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$"
Expand Down
6 changes: 3 additions & 3 deletions src/core/prompts/tools/edit-file.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// kilocode_change: Morph fast apply - file added

export function getFastApplyEditingInstructions(modelType: "Morph" | "Relace"): string {
return `- **${modelType} FastApply is enabled.** You have access to the \`edit_file\` tool which uses a specialized model optimized for intelligent code understanding and modification.
- **ONLY use the edit_file tool for file modifications.** Traditional editing tools (apply_diff, write_to_file, insert_content, search_and_replace) are disabled in ${modelType} mode.
export function getFastApplyEditingInstructions(): string {
return `- **FastApply is enabled.** You have access to the \`edit_file\` tool which uses a specialized model optimized for intelligent code understanding and modification.
- **ONLY use the edit_file tool for file modifications.** Traditional editing tools (apply_diff, write_to_file, insert_content, search_and_replace) are disabled in Fast Apply mode.
- **Focus on clear instructions and precise code edits** using the edit_file format with \`// ... existing code ...\` placeholders to represent unchanged sections.
- **The edit_file tool requires three parameters:**
- \`target_file\`: Full path to the file to modify
Expand Down
98 changes: 56 additions & 42 deletions src/core/tools/editFileTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { DEFAULT_HEADERS } from "../../api/providers/constants"
import { TelemetryService } from "@roo-code/telemetry"
import { type ClineProviderState } from "../webview/ClineProvider"
import { ClineSayTool } from "../../shared/ExtensionMessage"
import { ProviderSettingsManager } from "../config/ProviderSettingsManager"
import { X_KILOCODE_ORGANIZATIONID, X_KILOCODE_TASKID, X_KILOCODE_TESTER } from "../../shared/kilocode/headers"

const FAST_APPLY_MODEL_PRICING = {
Expand Down Expand Up @@ -227,16 +228,18 @@ async function applyFastApplyEdit(
filePath: string,
): Promise<MorphApplyResult> {
try {
// Get the current API configuration
const state = await cline.providerRef.deref()?.getState()
if (!state) {
return { success: false, error: "Unable to get provider state" }
}

const provider = cline.providerRef.deref()
if (!provider) {
return { success: false, error: "No API provider available for Fast Apply" }
return { success: false, error: "Unable to get provider reference" }
}

const state = await provider.getState()

// Check if user has Fast Apply enabled via OpenRouter or direct API
const morphConfig = await getFastApplyConfiguration(state)
const morphConfig = await getFastApplyConfiguration(state, provider.providerSettingsManager)
if (!morphConfig.available) {
return { success: false, error: morphConfig.error || "Fast Apply is not available" }
}
Expand Down Expand Up @@ -330,7 +333,10 @@ interface FastApplyConfiguration {
kiloCodeOrganizationId?: string
}

function getFastApplyConfiguration(state: ClineProviderState): FastApplyConfiguration {
async function getFastApplyConfiguration(
state: ClineProviderState,
providerSettingsManager: ProviderSettingsManager,
): Promise<FastApplyConfiguration> {
// Check if Fast Apply is enabled in API configuration
if (state.experiments.morphFastApply !== true) {
return {
Expand All @@ -341,6 +347,9 @@ function getFastApplyConfiguration(state: ClineProviderState): FastApplyConfigur

// Read the selected model from state
const selectedModel = state.fastApplyModel || "auto"
// Get the provider type and profile ID from state
const providerType = state.fastApplyProviderType || "morph"
const profileId = state.fastApplyProfileId

// Priority 1: Use direct Morph API key if available
// Allow human-relay for debugging
Expand All @@ -353,46 +362,51 @@ function getFastApplyConfiguration(state: ClineProviderState): FastApplyConfigur
model: org === "morph" ? model : "auto", // Use selected model instead of hardcoded "auto"
}
}

// Priority 2: Use KiloCode provider
if (state.apiConfiguration?.apiProvider === "kilocode") {
const token = state.apiConfiguration.kilocodeToken
if (!token) {
return { available: false, error: "No KiloCode token available to use Fast Apply" }
}
return {
available: true,
apiKey: token,
baseUrl: `${getKiloBaseUriFromToken(token)}/api/openrouter/`,
model: selectedModel === "auto" ? "morph/morph-v3-large" : selectedModel, // Use selected model
kiloCodeOrganizationId: state.apiConfiguration.kilocodeOrganizationId,
}
}

// Priority 3: Use OpenRouter provider
if (state.apiConfiguration?.apiProvider === "openrouter") {
const token = state.apiConfiguration.openRouterApiKey
if (!token) {
return { available: false, error: "No OpenRouter API token available to use Fast Apply" }
}
return {
available: true,
apiKey: token,
baseUrl: state.apiConfiguration.openRouterBaseUrl || "https://openrouter.ai/api/v1",
model: selectedModel === "auto" ? "morph/morph-v3-large" : selectedModel, // Use selected model
// Priority 2: Use profile-based configuration if provider type is openrouter or kilocode
if ((providerType === "openrouter" || providerType === "kilocode") && profileId) {
try {
// Get the profile details from the provider settings manager
const profile = await providerSettingsManager.getProfile({ id: profileId })

if (providerType === "kilocode") {
// KiloCode provider
const token = profile.kilocodeToken
if (!token) {
return { available: false, error: "No KiloCode token available in selected profile" }
}
return {
available: true,
apiKey: token,
baseUrl: `${getKiloBaseUriFromToken(token)}/api/openrouter/`,
model: selectedModel === "auto" ? "morph/morph-v3-large" : selectedModel, // Use selected model
kiloCodeOrganizationId: profile.kilocodeOrganizationId,
}
} else if (providerType === "openrouter") {
// OpenRouter provider
const token = profile.openRouterApiKey
if (!token) {
return { available: false, error: "No OpenRouter API token available in selected profile" }
}
return {
available: true,
apiKey: token,
baseUrl: profile.openRouterBaseUrl,
model: selectedModel === "auto" ? "morph/morph-v3-large" : selectedModel, // Use selected model
}
}
} catch (error) {
return {
available: false,
error: `Failed to load profile: ${error instanceof Error ? error.message : "Unknown error"}`,
}
}
}

return {
available: false,
error: "Fast Apply configuration error. Please check your settings.",
}
return { available: false, error: "No valid Fast Apply configuration found" }
}

export function isFastApplyAvailable(state?: ClineProviderState): boolean {
return (state && getFastApplyConfiguration(state).available) || false
}

export function getFastApplyModelType(state?: ClineProviderState): "Morph" | "Relace" {
return state && getFastApplyConfiguration(state).model?.startsWith("relace/") ? "Relace" : "Morph"
// For availability check, we only check if morphFastApply is enabled
// We don't validate the full configuration since we don't have providerSettingsManager here
return state?.experiments?.morphFastApply === true
}
6 changes: 6 additions & 0 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1906,6 +1906,8 @@ export class ClineProvider
dismissedNotificationIds, // kilocode_change
morphApiKey, // kilocode_change
fastApplyModel, // kilocode_change: Fast Apply model selection
fastApplyProviderType, // kilocode_change: Fast Apply provider type
fastApplyProfileId, // kilocode_change: Fast Apply profile ID
alwaysAllowFollowupQuestions,
followupAutoApproveTimeoutMs,
includeDiagnosticMessages,
Expand Down Expand Up @@ -2087,6 +2089,8 @@ export class ClineProvider
dismissedNotificationIds: dismissedNotificationIds ?? [], // kilocode_change
morphApiKey, // kilocode_change
fastApplyModel: fastApplyModel ?? "auto", // kilocode_change: Fast Apply model selection
fastApplyProviderType, // kilocode_change: Fast Apply provider type
fastApplyProfileId, // kilocode_change: Fast Apply profile ID
alwaysAllowFollowupQuestions: alwaysAllowFollowupQuestions ?? false,
followupAutoApproveTimeoutMs: followupAutoApproveTimeoutMs ?? 60000,
includeDiagnosticMessages: includeDiagnosticMessages ?? true,
Expand Down Expand Up @@ -2303,6 +2307,8 @@ export class ClineProvider
dismissedNotificationIds: stateValues.dismissedNotificationIds ?? [], // kilocode_change
morphApiKey: stateValues.morphApiKey, // kilocode_change
fastApplyModel: stateValues.fastApplyModel ?? "auto", // kilocode_change: Fast Apply model selection
fastApplyProviderType: stateValues.fastApplyProviderType, // kilocode_change: Fast Apply provider type selection
fastApplyProfileId: stateValues.fastApplyProfileId, // kilocode_change: Fast Apply profile ID
historyPreviewCollapsed: stateValues.historyPreviewCollapsed ?? false,
reasoningBlockCollapsed: stateValues.reasoningBlockCollapsed ?? true,
cloudUserInfo,
Expand Down
10 changes: 10 additions & 0 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1457,6 +1457,16 @@ export const webviewMessageHandler = async (
await provider.postStateToWebview()
break
}
case "fastApplyProviderType":
const providerType = message.text as "morph" | "openrouter" | "kilocode" | undefined
await updateGlobalState("fastApplyProviderType", providerType)
await provider.postStateToWebview()
break

case "fastApplyProfileId":
await updateGlobalState("fastApplyProfileId", message.text)
await provider.postStateToWebview()
break
// kilocode_change end
case "updateVSCodeSetting": {
const { setting, value } = message
Expand Down
2 changes: 2 additions & 0 deletions src/shared/ExtensionMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,8 @@ export type ExtensionState = Pick<
| "fuzzyMatchThreshold"
| "morphApiKey" // kilocode_change: Morph fast apply - global setting
| "fastApplyModel" // kilocode_change: Fast Apply model selection
| "fastApplyProviderType" // kilocode_change: Fast Apply provider type selection
| "fastApplyProfileId" // kilocode_change: Fast Apply profile ID
// | "experiments" // Optional in GlobalSettings, required here.
| "language"
// | "telemetrySetting" // Optional in GlobalSettings, required here.
Expand Down
2 changes: 2 additions & 0 deletions src/shared/WebviewMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ export interface WebviewMessage {
| "fuzzyMatchThreshold"
| "morphApiKey" // kilocode_change: Morph fast apply - global setting
| "fastApplyModel" // kilocode_change: Fast Apply model selection
| "fastApplyProviderType" // kilocode_change: Fast Apply provider type
| "fastApplyProfileId" // kilocode_change: Fast Apply profile ID
| "writeDelayMs"
| "diagnosticsEnabled"
| "enhancePrompt"
Expand Down
26 changes: 19 additions & 7 deletions webview-ui/src/components/settings/ExperimentalSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import { EXPERIMENT_IDS, experimentConfigsMap } from "@roo/experiments"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { cn } from "@src/lib/utils"

import {
SetCachedStateField, // kilocode_change
SetExperimentEnabled,
} from "./types"
import { SetExperimentEnabled } from "./types"
import { SectionHeader } from "./SectionHeader"
import { Section } from "./Section"
import { ExperimentalFeature } from "./ExperimentalFeature"
Expand All @@ -24,7 +21,12 @@ type ExperimentalSettingsProps = HTMLAttributes<HTMLDivElement> & {
// kilocode_change start
morphApiKey?: string
fastApplyModel?: string
setCachedStateField: SetCachedStateField<"morphApiKey" | "fastApplyModel">
fastApplyProviderType?: "morph" | "openrouter" | "kilocode"
fastApplyProfileId?: string
setMorphApiKey: (apiKey: string) => void
setFastApplyModel: (model: string) => void
setFastApplyProviderType: (providerType: "morph" | "openrouter" | "kilocode") => void
setFastApplyProfileId: (profileId: string) => void
kiloCodeImageApiKey?: string
setKiloCodeImageApiKey?: (apiKey: string) => void
currentProfileKilocodeToken?: string
Expand All @@ -50,7 +52,12 @@ export const ExperimentalSettings = ({
// kilocode_change start
morphApiKey,
fastApplyModel, // kilocode_change: Fast Apply model selection
setCachedStateField,
fastApplyProviderType,
fastApplyProfileId,
setMorphApiKey,
setFastApplyModel,
setFastApplyProviderType,
setFastApplyProfileId,
setKiloCodeImageApiKey,
kiloCodeImageApiKey,
currentProfileKilocodeToken,
Expand Down Expand Up @@ -104,9 +111,14 @@ export const ExperimentalSettings = ({
/>
{enabled && (
<FastApplySettings
setCachedStateField={setCachedStateField}
morphApiKey={morphApiKey}
fastApplyModel={fastApplyModel}
fastApplyProviderType={fastApplyProviderType}
fastApplyProfileId={fastApplyProfileId}
setMorphApiKey={setMorphApiKey}
setFastApplyModel={setFastApplyModel}
setFastApplyProviderType={setFastApplyProviderType}
setFastApplyProfileId={setFastApplyProfileId}
/>
)}
</React.Fragment>
Expand Down
Loading