Skip to content
Merged
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
10 changes: 10 additions & 0 deletions packages/types/src/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ export const historyItemSchema = z.object({
size: z.number().optional(),
workspace: z.string().optional(),
mode: z.string().optional(),
/**
* The tool protocol used by this task. Once a task uses tools with a specific
* protocol (XML or Native), it is permanently locked to that protocol.
*
* - "xml": Tool calls are parsed from XML text (no tool IDs)
* - "native": Tool calls come as tool_call chunks with IDs
*
* This ensures task resumption works correctly even when NTC settings change.
*/
toolProtocol: z.enum(["xml", "native"]).optional(),
status: z.enum(["active", "completed", "delegated"]).optional(),
delegatedToId: z.string().optional(), // Last child this parent delegated to
childIds: z.array(z.string()).optional(), // All children spawned by this task
Expand Down
3 changes: 2 additions & 1 deletion src/api/providers/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
// Enable native tools by default using resolveToolProtocol (which checks model's defaultToolProtocol)
// This matches OpenRouter's approach of always including tools when provided
// Also exclude tools when tool_choice is "none" since that means "don't use tools"
// IMPORTANT: Use metadata.toolProtocol if provided (task's locked protocol) for consistency
const model = this.getModel()
const toolProtocol = resolveToolProtocol(this.options, model.info)
const toolProtocol = resolveToolProtocol(this.options, model.info, metadata?.toolProtocol)
const shouldIncludeNativeTools =
metadata?.tools &&
metadata.tools.length > 0 &&
Expand Down
3 changes: 2 additions & 1 deletion src/api/providers/openrouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,8 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
}

// Process reasoning_details when switching models to Gemini for native tool call compatibility
const toolProtocol = resolveToolProtocol(this.options, model.info)
// IMPORTANT: Use metadata.toolProtocol if provided (task's locked protocol) for consistency
const toolProtocol = resolveToolProtocol(this.options, model.info, metadata?.toolProtocol)
const isNativeProtocol = toolProtocol === TOOL_PROTOCOL.NATIVE
const isGemini = modelId.startsWith("google/gemini")

Expand Down
3 changes: 2 additions & 1 deletion src/api/providers/requesty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ export class RequestyHandler extends BaseProvider implements SingleCompletionHan
: undefined

// Check if native tool protocol is enabled
const toolProtocol = resolveToolProtocol(this.options, info)
// IMPORTANT: Use metadata.toolProtocol if provided (task's locked protocol) for consistency
const toolProtocol = resolveToolProtocol(this.options, info, metadata?.toolProtocol)
const useNativeTools = toolProtocol === TOOL_PROTOCOL.NATIVE

const completionParams: RequestyChatCompletionParamsStreaming = {
Expand Down
7 changes: 5 additions & 2 deletions src/core/environment/getEnvironmentDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,12 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo
language: language ?? formatLanguage(vscode.env.language),
})

// Resolve and add tool protocol information
// Use the task's locked tool protocol for consistent environment details.
// This ensures the model sees the same tool format it was started with,
// even if user settings have changed. Fall back to resolving fresh if
// the task hasn't been fully initialized yet (shouldn't happen in practice).
const modelInfo = cline.api.getModel().info
const toolProtocol = resolveToolProtocol(state?.apiConfiguration ?? {}, modelInfo)
const toolProtocol = resolveToolProtocol(state?.apiConfiguration ?? {}, modelInfo, cline.taskToolProtocol)

details += `\n\n# Current Mode\n`
details += `<slug>${currentMode}</slug>\n`
Expand Down
11 changes: 10 additions & 1 deletion src/core/task-persistence/taskMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import NodeCache from "node-cache"
import getFolderSize from "get-folder-size"

import type { ClineMessage, HistoryItem } from "@roo-code/types"
import type { ClineMessage, HistoryItem, ToolProtocol } from "@roo-code/types"

import { combineApiRequests } from "../../shared/combineApiRequests"
import { combineCommandSequences } from "../../shared/combineCommandSequences"
Expand All @@ -23,6 +23,11 @@ export type TaskMetadataOptions = {
mode?: string
/** Initial status for the task (e.g., "active" for child tasks) */
initialStatus?: "active" | "delegated" | "completed"
/**
* The tool protocol locked to this task. Once set, the task will
* continue using this protocol even if user settings change.
*/
toolProtocol?: ToolProtocol
}

export async function taskMetadata({
Expand All @@ -35,6 +40,7 @@ export async function taskMetadata({
workspace,
mode,
initialStatus,
toolProtocol,
}: TaskMetadataOptions) {
const taskDir = await getTaskDirectoryPath(globalStoragePath, id)

Expand Down Expand Up @@ -90,6 +96,8 @@ export async function taskMetadata({
// initialStatus is included when provided (e.g., "active" for child tasks)
// to ensure the status is set from the very first save, avoiding race conditions
// where attempt_completion might run before a separate status update.
// toolProtocol is persisted to ensure tasks resume with the correct protocol
// even if user settings have changed.
const historyItem: HistoryItem = {
id,
rootTaskId,
Expand All @@ -107,6 +115,7 @@ export async function taskMetadata({
size: taskDirSize,
workspace,
mode,
...(toolProtocol && { toolProtocol }),
...(initialStatus && { status: initialStatus }),
}

Expand Down
Loading
Loading