@@ -133,10 +155,10 @@ export default function CookiePolicy() {
Analytics cookies
- We use PostHog analytics cookies to understand how visitors interact with our website. This
- helps us improve our services and user experience. Analytics cookies are placed only if you give
- consent through our cookie banner. The lawful basis for processing these cookies is your
- consent, which you can withdraw at any time.
+ We use PostHog and HubSpot analytics cookies to understand how visitors interact with our
+ website. This helps us improve our services, user experience, and marketing efforts. Analytics
+ cookies are placed only if you give consent through our cookie banner. The lawful basis for
+ processing these cookies is your consent, which you can withdraw at any time.
Third-party services
diff --git a/apps/web-roo-code/src/app/pricing/page.tsx b/apps/web-roo-code/src/app/pricing/page.tsx
index 487c14d087..6851b47b6a 100644
--- a/apps/web-roo-code/src/app/pricing/page.tsx
+++ b/apps/web-roo-code/src/app/pricing/page.tsx
@@ -291,11 +291,7 @@ export default function PricingPage() {
To pay for Cloud Agents running time (${PRICE_CREDITS}/hour)
To pay for AI model inference costs (
-
+
varies by model
)
diff --git a/apps/web-roo-code/src/app/slack/page.tsx b/apps/web-roo-code/src/app/slack/page.tsx
new file mode 100644
index 0000000000..c1fb39cb3e
--- /dev/null
+++ b/apps/web-roo-code/src/app/slack/page.tsx
@@ -0,0 +1,401 @@
+import {
+ ArrowRight,
+ Brain,
+ CreditCard,
+ GitBranch,
+ GraduationCap,
+ Link2,
+ MessageSquare,
+ Settings,
+ Shield,
+ Slack,
+ Users,
+ Zap,
+} from "lucide-react"
+import type { LucideIcon } from "lucide-react"
+import type { Metadata } from "next"
+
+import { AnimatedBackground } from "@/components/homepage"
+import { SlackThreadDemo } from "@/components/slack/slack-thread-demo"
+import { Button } from "@/components/ui"
+import { EXTERNAL_LINKS } from "@/lib/constants"
+import { SEO } from "@/lib/seo"
+import { ogImageUrl } from "@/lib/og"
+
+const TITLE = "Roo Code for Slack"
+const DESCRIPTION =
+ "Mention @Roomote in any channel to explain code, plan features, or ship a PR, all without leaving the conversation."
+const OG_DESCRIPTION = "Your AI Team in Slack"
+const PATH = "/slack"
+
+export const metadata: Metadata = {
+ title: TITLE,
+ description: DESCRIPTION,
+ alternates: {
+ canonical: `${SEO.url}${PATH}`,
+ },
+ openGraph: {
+ title: TITLE,
+ description: DESCRIPTION,
+ url: `${SEO.url}${PATH}`,
+ siteName: SEO.name,
+ images: [
+ {
+ url: ogImageUrl(TITLE, OG_DESCRIPTION),
+ width: 1200,
+ height: 630,
+ alt: TITLE,
+ },
+ ],
+ locale: SEO.locale,
+ type: "website",
+ },
+ twitter: {
+ card: SEO.twitterCard,
+ title: TITLE,
+ description: DESCRIPTION,
+ images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
+ },
+ keywords: [
+ ...SEO.keywords,
+ "slack integration",
+ "slack bot",
+ "AI in slack",
+ "code assistant slack",
+ "@Roomote",
+ "team collaboration",
+ ],
+}
+
+// Invalidate cache when a request comes in, at most once every hour.
+export const revalidate = 3600
+
+type ValueProp = {
+ icon: LucideIcon
+ title: string
+ description: string
+}
+
+const VALUE_PROPS: ValueProp[] = [
+ {
+ icon: GitBranch,
+ title: "Discussion to PR.",
+ description:
+ "Your team discusses a feature in Slack. @Roomote turns the discussion into a plan. Then builds it. All without leaving the conversation.",
+ },
+ {
+ icon: Brain,
+ title: "Thread-aware.",
+ description:
+ '@Roomote reads the full thread before responding. Ask "Can we add caching here?" and it knows exactly what code you mean.',
+ },
+ {
+ icon: Link2,
+ title: "Chain agents.",
+ description:
+ "Start with a Planner to spec it out. Then call the Coder to build it. Multi-step workflows, one Slack thread.",
+ },
+ {
+ icon: Users,
+ title: "Open to all.",
+ description:
+ "Anyone on your team can ask @Roomote to fix bugs, build features, or investigate issues. Engineering gets looped in only when needed.",
+ },
+ {
+ icon: GraduationCap,
+ title: "Built-in learning.",
+ description: "Public channel mentions show everyone how to leverage agents. Learn by watching.",
+ },
+ {
+ icon: Shield,
+ title: "Safe by design.",
+ description: "Agents never touch main/master directly. They produce branches and PRs. You approve.",
+ },
+]
+
+type WorkflowStep = {
+ step: number
+ title: string
+ description: string
+}
+
+const WORKFLOW_STEPS: WorkflowStep[] = [
+ {
+ step: 1,
+ title: "Turn the discussion into a plan",
+ description: "Your team discusses a feature. When it gets complex, summon the Planner agent.",
+ },
+ {
+ step: 2,
+ title: "Refine the plan in the thread",
+ description:
+ "The team reviews the spec in the thread, suggests changes, asks questions. Mention @Roomote again to refine.",
+ },
+ {
+ step: 3,
+ title: "Build the plan",
+ description: "Once the plan looks good, hand it off to the Coder agent to implement.",
+ },
+ {
+ step: 4,
+ title: "Review and ship",
+ description: "The Coder creates a branch and opens a PR. The team reviews, and the feature ships.",
+ },
+]
+
+type OnboardingStep = {
+ icon: LucideIcon
+ title: string
+ description: string
+ link?: {
+ href: string
+ text: string
+ }
+}
+
+const ONBOARDING_STEPS: OnboardingStep[] = [
+ {
+ icon: CreditCard,
+ title: "1. Team Plan",
+ description: "Slack requires a Team plan.",
+ link: {
+ href: EXTERNAL_LINKS.CLOUD_APP_TEAM_TRIAL,
+ text: "Start a free trial",
+ },
+ },
+ {
+ icon: Settings,
+ title: "2. Connect",
+ description: 'Sign in to Roo Code Cloud and go to Settings. Click "Connect" next to Slack.',
+ },
+ {
+ icon: Slack,
+ title: "3. Authorize",
+ description: "Authorize the Roo Code app to access your Slack workspace.",
+ },
+ {
+ icon: MessageSquare,
+ title: "4. Add to channels",
+ description: "Add @Roomote to the channels where you want it available.",
+ },
+]
+
+export default function SlackPage(): JSX.Element {
+ return (
+ <>
+ {/* Hero Section */}
+
+
+
+
+
+
+
+ Powered by Roo Code Cloud
+
+
+ @Roomote: Your AI Team in Slack
+
+
+ Mention @Roomote in any channel to explain code, plan features, or ship a PR, all
+ without leaving the conversation.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Value Props Section */}
+
+
+
+
+
+
+
+ Why your team will love using Roo Code in Slack
+
+
+ AI agents that understand context, chain together for complex work, and keep your team in
+ control.
+
+ I've created a comprehensive implementation plan for the Roo Code Slack integration
+ marketing page at{" "}
+
+ plans/slack-marketing-page-plan.md
+
+ .
+
+
+
Plan Overview
+
+
+ •Hero + dual CTAs
+
+
+ •Value props grid
+
+
+ •“Thread to Shipped Feature” workflow
+
+
+ •Onboarding steps + CTA
+
+
+
+
+ Full document:
+ View artifact
+
+
+ Want to follow up? Just @-mention me in your response.
+
")
- expect(toolUse.params.path).toBe("src")
- expect(toolUse.partial).toBe(false)
- })
-
- it("should handle consecutive tool uses without text in between", () => {
- const message =
- "file1.tsfile2.ts"
- const result = parser(message).filter((block) => !isEmptyTextContent(block))
-
- expect(result).toHaveLength(2)
-
- const toolUse1 = result[0] as ToolUse
- expect(toolUse1.type).toBe("tool_use")
- expect(toolUse1.name).toBe("read_file")
- expect(toolUse1.params.path).toBe("file1.ts")
- expect(toolUse1.partial).toBe(false)
-
- const toolUse2 = result[1] as ToolUse
- expect(toolUse2.type).toBe("tool_use")
- expect(toolUse2.name).toBe("read_file")
- expect(toolUse2.params.path).toBe("file2.ts")
- expect(toolUse2.partial).toBe(false)
- })
-
- it("should handle whitespace in parameters", () => {
- const message = " src/file.ts "
- const result = parser(message).filter((block) => !isEmptyTextContent(block))
-
- expect(result).toHaveLength(1)
- const toolUse = result[0] as ToolUse
- expect(toolUse.type).toBe("tool_use")
- expect(toolUse.name).toBe("read_file")
- expect(toolUse.params.path).toBe("src/file.ts")
- expect(toolUse.partial).toBe(false)
- })
-
- it("should handle multi-line parameters", () => {
- const message = `file.ts
- line 1
- line 2
- line 3
- `
- const result = parser(message).filter((block) => !isEmptyTextContent(block))
-
- expect(result).toHaveLength(1)
- const toolUse = result[0] as ToolUse
- expect(toolUse.type).toBe("tool_use")
- expect(toolUse.name).toBe("write_to_file")
- expect(toolUse.params.path).toBe("file.ts")
- expect(toolUse.params.content).toContain("line 1")
- expect(toolUse.params.content).toContain("line 2")
- expect(toolUse.params.content).toContain("line 3")
- expect(toolUse.partial).toBe(false)
- })
-
- it("should handle a complex message with multiple content types", () => {
- const message = `I'll help you with that task.
-
- src/index.ts
-
- Now let's modify the file:
-
- src/index.ts
- // Updated content
- console.log("Hello world");
-
-
- Let's run the code:
-
- node src/index.ts`
-
- const result = parser(message)
-
- expect(result).toHaveLength(6)
-
- // First text block
- expect(result[0].type).toBe("text")
- expect((result[0] as TextContent).content).toBe("I'll help you with that task.")
-
- // First tool use (read_file)
- expect(result[1].type).toBe("tool_use")
- expect((result[1] as ToolUse).name).toBe("read_file")
-
- // Second text block
- expect(result[2].type).toBe("text")
- expect((result[2] as TextContent).content).toContain("Now let's modify the file:")
-
- // Second tool use (write_to_file)
- expect(result[3].type).toBe("tool_use")
- expect((result[3] as ToolUse).name).toBe("write_to_file")
-
- // Third text block
- expect(result[4].type).toBe("text")
- expect((result[4] as TextContent).content).toContain("Let's run the code:")
-
- // Third tool use (execute_command)
- expect(result[5].type).toBe("tool_use")
- expect((result[5] as ToolUse).name).toBe("execute_command")
- })
- })
- })
-})
diff --git a/src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts b/src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts
deleted file mode 100644
index a32b1173ce..0000000000
--- a/src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts
+++ /dev/null
@@ -1,111 +0,0 @@
-/* eslint-disable @typescript-eslint/no-unsafe-function-type */
-
-// node --expose-gc --import tsx src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts
-
-import { performance } from "perf_hooks"
-import { parseAssistantMessage as parseAssistantMessageV1 } from "../parseAssistantMessage"
-import { parseAssistantMessageV2 } from "../parseAssistantMessageV2"
-
-const formatNumber = (num: number): string => {
- return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
-}
-
-const measureExecutionTime = (fn: Function, input: string, iterations: number = 1000): number => {
- for (let i = 0; i < 10; i++) {
- fn(input)
- }
-
- const start = performance.now()
-
- for (let i = 0; i < iterations; i++) {
- fn(input)
- }
-
- const end = performance.now()
- return (end - start) / iterations // Average time per iteration in ms.
-}
-
-const measureMemoryUsage = (
- fn: Function,
- input: string,
- iterations: number = 100,
-): { heapUsed: number; heapTotal: number } => {
- if (global.gc) {
- // Force garbage collection if available.
- global.gc()
- } else {
- console.warn("No garbage collection hook! Run with --expose-gc for more accurate memory measurements.")
- }
-
- const initialMemory = process.memoryUsage()
-
- for (let i = 0; i < iterations; i++) {
- fn(input)
- }
-
- const finalMemory = process.memoryUsage()
-
- return {
- heapUsed: (finalMemory.heapUsed - initialMemory.heapUsed) / iterations,
- heapTotal: (finalMemory.heapTotal - initialMemory.heapTotal) / iterations,
- }
-}
-
-const testCases = [
- {
- name: "Simple text message",
- input: "This is a simple text message without any tool uses.",
- },
- {
- name: "Message with a simple tool use",
- input: "Let's read a file: src/file.ts",
- },
- {
- name: "Message with a complex tool use (write_to_file)",
- input: "src/file.ts\nfunction example() {\n // This has XML-like content: \n return true;\n}\n",
- },
- {
- name: "Message with multiple tool uses",
- input: "First file: src/file1.ts\nSecond file: src/file2.ts\nLet's write a new file: src/file3.ts\nexport function newFunction() {\n return 'Hello world';\n}\n",
- },
- {
- name: "Large message with repeated tool uses",
- input: Array(50)
- .fill(
- 'src/file.ts\noutput.tsconsole.log("hello");',
- )
- .join("\n"),
- },
-]
-
-const runBenchmark = () => {
- const maxNameLength = testCases.reduce((max, testCase) => Math.max(max, testCase.name.length), 0)
- const namePadding = maxNameLength + 2
-
- console.log(
- `| ${"Test Case".padEnd(namePadding)} | V1 Time (ms) | V2 Time (ms) | V1/V2 Ratio | V1 Heap (bytes) | V2 Heap (bytes) |`,
- )
- console.log(
- `| ${"-".repeat(namePadding)} | ------------ | ------------ | ----------- | ---------------- | ---------------- |`,
- )
-
- for (const testCase of testCases) {
- const v1Time = measureExecutionTime(parseAssistantMessageV1, testCase.input)
- const v2Time = measureExecutionTime(parseAssistantMessageV2, testCase.input)
- const timeRatio = v1Time / v2Time
-
- const v1Memory = measureMemoryUsage(parseAssistantMessageV1, testCase.input)
- const v2Memory = measureMemoryUsage(parseAssistantMessageV2, testCase.input)
-
- console.log(
- `| ${testCase.name.padEnd(namePadding)} | ` +
- `${v1Time.toFixed(4).padStart(12)} | ` +
- `${v2Time.toFixed(4).padStart(12)} | ` +
- `${timeRatio.toFixed(2).padStart(11)} | ` +
- `${formatNumber(Math.round(v1Memory.heapUsed)).padStart(16)} | ` +
- `${formatNumber(Math.round(v2Memory.heapUsed)).padStart(16)} |`,
- )
- }
-}
-
-runBenchmark()
diff --git a/src/core/assistant-message/__tests__/presentAssistantMessage-custom-tool.spec.ts b/src/core/assistant-message/__tests__/presentAssistantMessage-custom-tool.spec.ts
index e90646fd9a..18e277905f 100644
--- a/src/core/assistant-message/__tests__/presentAssistantMessage-custom-tool.spec.ts
+++ b/src/core/assistant-message/__tests__/presentAssistantMessage-custom-tool.spec.ts
@@ -7,6 +7,11 @@ import { presentAssistantMessage } from "../presentAssistantMessage"
vi.mock("../../task/Task")
vi.mock("../../tools/validateToolUse", () => ({
validateToolUse: vi.fn(),
+ isValidToolName: vi.fn((toolName: string) =>
+ ["read_file", "write_to_file", "ask_followup_question", "attempt_completion", "use_mcp_tool"].includes(
+ toolName,
+ ),
+ ),
}))
// Mock custom tool registry - must be done inline without external variable references
@@ -116,39 +121,7 @@ describe("presentAssistantMessage - Custom Tool Recording", () => {
// Should record as "custom_tool", not "my_custom_tool"
expect(mockTask.recordToolUsage).toHaveBeenCalledWith("custom_tool")
- expect(TelemetryService.instance.captureToolUsage).toHaveBeenCalledWith(
- mockTask.taskId,
- "custom_tool",
- "native",
- )
- })
-
- it("should record custom tool usage as 'custom_tool' in XML protocol", async () => {
- mockTask.assistantMessageContent = [
- {
- type: "tool_use",
- // No ID = XML protocol
- name: "my_custom_tool",
- params: { value: "test" },
- partial: false,
- },
- ]
-
- vi.mocked(customToolRegistry.has).mockReturnValue(true)
- vi.mocked(customToolRegistry.get).mockReturnValue({
- name: "my_custom_tool",
- description: "A custom tool",
- execute: vi.fn().mockResolvedValue("Custom tool result"),
- })
-
- await presentAssistantMessage(mockTask)
-
- expect(mockTask.recordToolUsage).toHaveBeenCalledWith("custom_tool")
- expect(TelemetryService.instance.captureToolUsage).toHaveBeenCalledWith(
- mockTask.taskId,
- "custom_tool",
- "xml",
- )
+ expect(TelemetryService.instance.captureToolUsage).toHaveBeenCalledWith(mockTask.taskId, "custom_tool")
})
})
@@ -201,11 +174,7 @@ describe("presentAssistantMessage - Custom Tool Recording", () => {
// Should record as "read_file", not "custom_tool"
expect(mockTask.recordToolUsage).toHaveBeenCalledWith("read_file")
- expect(TelemetryService.instance.captureToolUsage).toHaveBeenCalledWith(
- mockTask.taskId,
- "read_file",
- "native",
- )
+ expect(TelemetryService.instance.captureToolUsage).toHaveBeenCalledWith(mockTask.taskId, "read_file")
})
it("should record MCP tool usage as 'use_mcp_tool' (not custom_tool)", async () => {
@@ -247,11 +216,7 @@ describe("presentAssistantMessage - Custom Tool Recording", () => {
// Should record as "use_mcp_tool", not "custom_tool"
expect(mockTask.recordToolUsage).toHaveBeenCalledWith("use_mcp_tool")
- expect(TelemetryService.instance.captureToolUsage).toHaveBeenCalledWith(
- mockTask.taskId,
- "use_mcp_tool",
- "native",
- )
+ expect(TelemetryService.instance.captureToolUsage).toHaveBeenCalledWith(mockTask.taskId, "use_mcp_tool")
})
})
diff --git a/src/core/assistant-message/__tests__/presentAssistantMessage-images.spec.ts b/src/core/assistant-message/__tests__/presentAssistantMessage-images.spec.ts
index 72ee430609..6740f780ed 100644
--- a/src/core/assistant-message/__tests__/presentAssistantMessage-images.spec.ts
+++ b/src/core/assistant-message/__tests__/presentAssistantMessage-images.spec.ts
@@ -4,12 +4,16 @@ import { describe, it, expect, beforeEach, vi } from "vitest"
import { Anthropic } from "@anthropic-ai/sdk"
import { presentAssistantMessage } from "../presentAssistantMessage"
import { Task } from "../../task/Task"
-import { TOOL_PROTOCOL } from "@roo-code/types"
// Mock dependencies
vi.mock("../../task/Task")
vi.mock("../../tools/validateToolUse", () => ({
validateToolUse: vi.fn(),
+ isValidToolName: vi.fn((toolName: string) =>
+ ["read_file", "write_to_file", "ask_followup_question", "attempt_completion", "use_mcp_tool"].includes(
+ toolName,
+ ),
+ ),
}))
vi.mock("@roo-code/telemetry", () => ({
TelemetryService: {
@@ -20,7 +24,7 @@ vi.mock("@roo-code/telemetry", () => ({
},
}))
-describe("presentAssistantMessage - Image Handling in Native Tool Calls", () => {
+describe("presentAssistantMessage - Image Handling in Native Tool Calling", () => {
let mockTask: any
beforeEach(() => {
@@ -74,15 +78,16 @@ describe("presentAssistantMessage - Image Handling in Native Tool Calls", () =>
})
})
- it("should preserve images in tool_result for native protocol", async () => {
- // Set up a tool_use block with an ID (indicates native protocol)
+ it("should preserve images in tool_result for native tool calling", async () => {
+ // Set up a tool_use block with an ID (indicates native tool calling)
const toolCallId = "tool_call_123"
mockTask.assistantMessageContent = [
{
type: "tool_use",
- id: toolCallId, // ID indicates native protocol
+ id: toolCallId, // ID indicates native tool calling
name: "ask_followup_question",
params: { question: "What do you see?" },
+ nativeArgs: { question: "What do you see?", follow_up: [] },
},
]
@@ -116,7 +121,7 @@ describe("presentAssistantMessage - Image Handling in Native Tool Calls", () =>
expect(toolResult).toBeDefined()
expect(toolResult.tool_use_id).toBe(toolCallId)
- // For native protocol, tool_result content should be a string (text only)
+ // For native tool calling, tool_result content should be a string (text only)
expect(typeof toolResult.content).toBe("string")
expect(toolResult.content).toContain("I see a cat")
@@ -126,7 +131,7 @@ describe("presentAssistantMessage - Image Handling in Native Tool Calls", () =>
expect(imageBlocks[0].source.data).toBe("base64ImageData")
})
- it("should convert to string when no images are present (native protocol)", async () => {
+ it("should convert to string when no images are present (native tool calling)", async () => {
// Set up a tool_use block with an ID (indicates native protocol)
const toolCallId = "tool_call_456"
mockTask.assistantMessageContent = [
@@ -135,6 +140,7 @@ describe("presentAssistantMessage - Image Handling in Native Tool Calls", () =>
id: toolCallId,
name: "ask_followup_question",
params: { question: "What is your name?" },
+ nativeArgs: { question: "What is your name?", follow_up: [] },
},
]
@@ -157,12 +163,11 @@ describe("presentAssistantMessage - Image Handling in Native Tool Calls", () =>
expect(typeof toolResult.content).toBe("string")
})
- it("should preserve images in content array for XML protocol (existing behavior)", async () => {
- // Set up a tool_use block WITHOUT an ID (indicates XML protocol)
+ it("should fail fast when tool_use is missing id (legacy/XML-style tool call)", async () => {
+ // tool_use without an id is treated as legacy/XML-style tool call and must be rejected.
mockTask.assistantMessageContent = [
{
type: "tool_use",
- // No ID = XML protocol
name: "ask_followup_question",
params: { question: "What do you see?" },
},
@@ -176,14 +181,13 @@ describe("presentAssistantMessage - Image Handling in Native Tool Calls", () =>
await presentAssistantMessage(mockTask)
- // For XML protocol, content is added as separate blocks
- // Check that both text and image blocks were added
- const hasTextBlock = mockTask.userMessageContent.some((item: any) => item.type === "text")
- const hasImageBlock = mockTask.userMessageContent.some((item: any) => item.type === "image")
-
- expect(hasTextBlock).toBe(true)
- // XML protocol preserves images as separate blocks in userMessageContent
- expect(hasImageBlock).toBe(true)
+ const textBlocks = mockTask.userMessageContent.filter((item: any) => item.type === "text")
+ expect(textBlocks.length).toBeGreaterThan(0)
+ expect(textBlocks.some((b: any) => String(b.text).includes("XML tool calls are no longer supported"))).toBe(
+ true,
+ )
+ // Should not proceed to execute tool or add images as tool output.
+ expect(mockTask.userMessageContent.some((item: any) => item.type === "image")).toBe(false)
})
it("should handle empty tool result gracefully", async () => {
@@ -216,7 +220,7 @@ describe("presentAssistantMessage - Image Handling in Native Tool Calls", () =>
})
describe("Multiple tool calls handling", () => {
- it("should send tool_result with is_error for skipped tools in native protocol when didRejectTool is true", async () => {
+ it("should send tool_result with is_error for skipped tools in native tool calling when didRejectTool is true", async () => {
// Simulate multiple tool calls with native protocol (all have IDs)
const toolCallId1 = "tool_call_001"
const toolCallId2 = "tool_call_002"
@@ -261,7 +265,7 @@ describe("presentAssistantMessage - Image Handling in Native Tool Calls", () =>
expect(textBlocks.length).toBe(0)
})
- it("should send tool_result with is_error for skipped tools in native protocol when didAlreadyUseTool is true", async () => {
+ it("should send tool_result with is_error for skipped tools in native tool calling when didAlreadyUseTool is true", async () => {
// Simulate multiple tool calls with native protocol
const toolCallId1 = "tool_call_003"
const toolCallId2 = "tool_call_004"
@@ -306,18 +310,15 @@ describe("presentAssistantMessage - Image Handling in Native Tool Calls", () =>
expect(textBlocks.length).toBe(0)
})
- it("should send text blocks for skipped tools in XML protocol (no tool IDs)", async () => {
- // Simulate multiple tool calls with XML protocol (no IDs)
+ it("should reject subsequent tool calls when a legacy/XML-style tool call is encountered", async () => {
mockTask.assistantMessageContent = [
{
type: "tool_use",
- // No ID = XML protocol
name: "read_file",
params: { path: "test.txt" },
},
{
type: "tool_use",
- // No ID = XML protocol
name: "write_to_file",
params: { path: "output.txt", content: "test" },
},
@@ -330,18 +331,15 @@ describe("presentAssistantMessage - Image Handling in Native Tool Calls", () =>
mockTask.currentStreamingContentIndex = 1
await presentAssistantMessage(mockTask)
- // For XML protocol, should add text block (not tool_result)
- const textBlocks = mockTask.userMessageContent.filter(
- (item: any) => item.type === "text" && item.text.includes("due to user rejecting"),
+ const textBlocks = mockTask.userMessageContent.filter((item: any) => item.type === "text")
+ expect(textBlocks.some((b: any) => String(b.text).includes("XML tool calls are no longer supported"))).toBe(
+ true,
)
- expect(textBlocks.length).toBeGreaterThan(0)
-
// Ensure no tool_result blocks were added
- const toolResults = mockTask.userMessageContent.filter((item: any) => item.type === "tool_result")
- expect(toolResults.length).toBe(0)
+ expect(mockTask.userMessageContent.some((item: any) => item.type === "tool_result")).toBe(false)
})
- it("should handle partial tool blocks when didRejectTool is true in native protocol", async () => {
+ it("should handle partial tool blocks when didRejectTool is true in native tool calling", async () => {
const toolCallId = "tool_call_005"
mockTask.assistantMessageContent = [
diff --git a/src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts b/src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts
index d4ae2764a0..e4a50be925 100644
--- a/src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts
+++ b/src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts
@@ -7,6 +7,7 @@ import { presentAssistantMessage } from "../presentAssistantMessage"
vi.mock("../../task/Task")
vi.mock("../../tools/validateToolUse", () => ({
validateToolUse: vi.fn(),
+ isValidToolName: vi.fn(() => false),
}))
vi.mock("@roo-code/telemetry", () => ({
TelemetryService: {
@@ -74,12 +75,12 @@ describe("presentAssistantMessage - Unknown Tool Handling", () => {
})
it("should return error for unknown tool in native protocol", async () => {
- // Set up a tool_use block with an unknown tool name and an ID (native protocol)
+ // Set up a tool_use block with an unknown tool name and an ID (native tool calling)
const toolCallId = "tool_call_unknown_123"
mockTask.assistantMessageContent = [
{
type: "tool_use",
- id: toolCallId, // ID indicates native protocol
+ id: toolCallId, // ID indicates native tool calling
name: "nonexistent_tool",
params: { some: "param" },
partial: false,
@@ -114,12 +115,11 @@ describe("presentAssistantMessage - Unknown Tool Handling", () => {
expect(mockTask.say).toHaveBeenCalledWith("error", "unknownToolError")
})
- it("should return error for unknown tool in XML protocol", async () => {
- // Set up a tool_use block with an unknown tool name WITHOUT an ID (XML protocol)
+ it("should fail fast when tool_use is missing id (legacy/XML-style tool call)", async () => {
+ // tool_use without an id is treated as legacy/XML-style tool call and must be rejected.
mockTask.assistantMessageContent = [
{
type: "tool_use",
- // No ID = XML protocol
name: "fake_tool_that_does_not_exist",
params: { param1: "value1" },
partial: false,
@@ -129,16 +129,12 @@ describe("presentAssistantMessage - Unknown Tool Handling", () => {
// Execute presentAssistantMessage
await presentAssistantMessage(mockTask)
- // For XML protocol, error is pushed as text blocks
+ // Should not execute tool; should surface a clear error message.
const textBlocks = mockTask.userMessageContent.filter((item: any) => item.type === "text")
-
- // There should be text blocks with error message
expect(textBlocks.length).toBeGreaterThan(0)
- const hasErrorMessage = textBlocks.some(
- (block: any) =>
- block.text?.includes("fake_tool_that_does_not_exist") && block.text?.includes("does not exist"),
+ expect(textBlocks.some((b: any) => String(b.text).includes("XML tool calls are no longer supported"))).toBe(
+ true,
)
- expect(hasErrorMessage).toBe(true)
// Verify consecutiveMistakeCount was incremented
expect(mockTask.consecutiveMistakeCount).toBe(1)
@@ -146,17 +142,17 @@ describe("presentAssistantMessage - Unknown Tool Handling", () => {
// Verify recordToolError was called
expect(mockTask.recordToolError).toHaveBeenCalled()
- // Verify error message was shown to user (uses i18n key)
- expect(mockTask.say).toHaveBeenCalledWith("error", "unknownToolError")
+ // Verify error message was shown to user
+ expect(mockTask.say).toHaveBeenCalledWith("error", expect.anything())
})
- it("should handle unknown tool without freezing (native protocol)", async () => {
+ it("should handle unknown tool without freezing (native tool calling)", async () => {
// This test ensures the extension doesn't freeze when an unknown tool is called
const toolCallId = "tool_call_freeze_test"
mockTask.assistantMessageContent = [
{
type: "tool_use",
- id: toolCallId, // Native protocol
+ id: toolCallId, // Native tool calling
name: "this_tool_definitely_does_not_exist",
params: {},
partial: false,
diff --git a/src/core/assistant-message/index.ts b/src/core/assistant-message/index.ts
index 72201b7722..107424fc50 100644
--- a/src/core/assistant-message/index.ts
+++ b/src/core/assistant-message/index.ts
@@ -1,2 +1,2 @@
-export { type AssistantMessageContent, parseAssistantMessage } from "./parseAssistantMessage"
+export type { AssistantMessageContent } from "./types"
export { presentAssistantMessage } from "./presentAssistantMessage"
diff --git a/src/core/assistant-message/parseAssistantMessage.ts b/src/core/assistant-message/parseAssistantMessage.ts
deleted file mode 100644
index e07b8cc3db..0000000000
--- a/src/core/assistant-message/parseAssistantMessage.ts
+++ /dev/null
@@ -1,166 +0,0 @@
-import { type ToolName, toolNames } from "@roo-code/types"
-
-import { TextContent, ToolUse, McpToolUse, ToolParamName, toolParamNames } from "../../shared/tools"
-
-export type AssistantMessageContent = TextContent | ToolUse | McpToolUse
-
-export function parseAssistantMessage(assistantMessage: string): AssistantMessageContent[] {
- let contentBlocks: AssistantMessageContent[] = []
- let currentTextContent: TextContent | undefined = undefined
- let currentTextContentStartIndex = 0
- let currentToolUse: ToolUse | undefined = undefined
- let currentToolUseStartIndex = 0
- let currentParamName: ToolParamName | undefined = undefined
- let currentParamValueStartIndex = 0
- let accumulator = ""
-
- for (let i = 0; i < assistantMessage.length; i++) {
- const char = assistantMessage[i]
- accumulator += char
-
- // There should not be a param without a tool use.
- if (currentToolUse && currentParamName) {
- const currentParamValue = accumulator.slice(currentParamValueStartIndex)
- const paramClosingTag = `${currentParamName}>`
- if (currentParamValue.endsWith(paramClosingTag)) {
- // End of param value.
- // Don't trim content parameters to preserve newlines, but strip first and last newline only
- const paramValue = currentParamValue.slice(0, -paramClosingTag.length)
- currentToolUse.params[currentParamName] =
- currentParamName === "content"
- ? paramValue.replace(/^\n/, "").replace(/\n$/, "")
- : paramValue.trim()
- currentParamName = undefined
- continue
- } else {
- // Partial param value is accumulating.
- continue
- }
- }
-
- // No currentParamName.
-
- if (currentToolUse) {
- const currentToolValue = accumulator.slice(currentToolUseStartIndex)
- const toolUseClosingTag = `${currentToolUse.name}>`
- if (currentToolValue.endsWith(toolUseClosingTag)) {
- // End of a tool use.
- currentToolUse.partial = false
- contentBlocks.push(currentToolUse)
- currentToolUse = undefined
- continue
- } else {
- const possibleParamOpeningTags = toolParamNames.map((name) => `<${name}>`)
- for (const paramOpeningTag of possibleParamOpeningTags) {
- if (accumulator.endsWith(paramOpeningTag)) {
- // Start of a new parameter.
- currentParamName = paramOpeningTag.slice(1, -1) as ToolParamName
- currentParamValueStartIndex = accumulator.length
- break
- }
- }
-
- // There's no current param, and not starting a new param.
-
- // Special case for write_to_file where file contents could
- // contain the closing tag, in which case the param would have
- // closed and we end up with the rest of the file contents here.
- // To work around this, we get the string between the starting
- // content tag and the LAST content tag.
- const contentParamName: ToolParamName = "content"
-
- if (currentToolUse.name === "write_to_file" && accumulator.endsWith(`${contentParamName}>`)) {
- const toolContent = accumulator.slice(currentToolUseStartIndex)
- const contentStartTag = `<${contentParamName}>`
- const contentEndTag = `${contentParamName}>`
- const contentStartIndex = toolContent.indexOf(contentStartTag) + contentStartTag.length
- const contentEndIndex = toolContent.lastIndexOf(contentEndTag)
-
- if (contentStartIndex !== -1 && contentEndIndex !== -1 && contentEndIndex > contentStartIndex) {
- // Don't trim content to preserve newlines, but strip first and last newline only
- currentToolUse.params[contentParamName] = toolContent
- .slice(contentStartIndex, contentEndIndex)
- .replace(/^\n/, "")
- .replace(/\n$/, "")
- }
- }
-
- // Partial tool value is accumulating.
- continue
- }
- }
-
- // No currentToolUse.
-
- let didStartToolUse = false
- const possibleToolUseOpeningTags = toolNames.map((name) => `<${name}>`)
-
- for (const toolUseOpeningTag of possibleToolUseOpeningTags) {
- if (accumulator.endsWith(toolUseOpeningTag)) {
- // Start of a new tool use.
- currentToolUse = {
- type: "tool_use",
- name: toolUseOpeningTag.slice(1, -1) as ToolName,
- params: {},
- partial: true,
- }
-
- currentToolUseStartIndex = accumulator.length
-
- // This also indicates the end of the current text content.
- if (currentTextContent) {
- currentTextContent.partial = false
-
- // Remove the partially accumulated tool use tag from the
- // end of text (()
- const toolParamOpenTags = new Map()
-
- for (const name of toolNames) {
- toolUseOpenTags.set(`<${name}>`, name)
- }
-
- for (const name of toolParamNames) {
- toolParamOpenTags.set(`<${name}>`, name)
- }
-
- const len = assistantMessage.length
-
- for (let i = 0; i < len; i++) {
- const currentCharIndex = i
-
- // Parsing a tool parameter
- if (currentToolUse && currentParamName) {
- const closeTag = `${currentParamName}>`
- // Check if the string *ending* at index `i` matches the closing tag
- if (
- currentCharIndex >= closeTag.length - 1 &&
- assistantMessage.startsWith(
- closeTag,
- currentCharIndex - closeTag.length + 1, // Start checking from potential start of tag.
- )
- ) {
- // Found the closing tag for the parameter.
- const value = assistantMessage.slice(
- currentParamValueStart, // Start after the opening tag.
- currentCharIndex - closeTag.length + 1, // End before the closing tag.
- )
- // Don't trim content parameters to preserve newlines, but strip first and last newline only
- currentToolUse.params[currentParamName] =
- currentParamName === "content" ? value.replace(/^\n/, "").replace(/\n$/, "") : value.trim()
- currentParamName = undefined // Go back to parsing tool content.
- // We don't continue loop here, need to check for tool close or other params at index i.
- } else {
- continue // Still inside param value, move to next char.
- }
- }
-
- // Parsing a tool use (but not a specific parameter).
- if (currentToolUse && !currentParamName) {
- // Ensure we are not inside a parameter already.
- // Check if starting a new parameter.
- let startedNewParam = false
-
- for (const [tag, paramName] of toolParamOpenTags.entries()) {
- if (
- currentCharIndex >= tag.length - 1 &&
- assistantMessage.startsWith(tag, currentCharIndex - tag.length + 1)
- ) {
- currentParamName = paramName
- currentParamValueStart = currentCharIndex + 1 // Value starts after the tag.
- startedNewParam = true
- break
- }
- }
-
- if (startedNewParam) {
- continue // Handled start of param, move to next char.
- }
-
- // Check if closing the current tool use.
- const toolCloseTag = `${currentToolUse.name}>`
-
- if (
- currentCharIndex >= toolCloseTag.length - 1 &&
- assistantMessage.startsWith(toolCloseTag, currentCharIndex - toolCloseTag.length + 1)
- ) {
- // End of the tool use found.
- // Special handling for content params *before* finalizing the
- // tool.
- const toolContentSlice = assistantMessage.slice(
- currentToolUseStart, // From after the tool opening tag.
- currentCharIndex - toolCloseTag.length + 1, // To before the tool closing tag.
- )
-
- // Check if content parameter needs special handling
- // (write_to_file/new_rule).
- // This check is important if the closing tag was
- // missed by the parameter parsing logic (e.g., if content is
- // empty or parsing logic prioritizes tool close).
- const contentParamName: ToolParamName = "content"
- if (
- currentToolUse.name === "write_to_file" /* || currentToolUse.name === "new_rule" */ &&
- // !(contentParamName in currentToolUse.params) && // Only if not already parsed.
- toolContentSlice.includes(`<${contentParamName}>`) // Check if tag exists.
- ) {
- const contentStartTag = `<${contentParamName}>`
- const contentEndTag = `${contentParamName}>`
- const contentStart = toolContentSlice.indexOf(contentStartTag)
-
- // Use `lastIndexOf` for robustness against nested tags.
- const contentEnd = toolContentSlice.lastIndexOf(contentEndTag)
-
- if (contentStart !== -1 && contentEnd !== -1 && contentEnd > contentStart) {
- // Don't trim content to preserve newlines, but strip first and last newline only
- const contentValue = toolContentSlice
- .slice(contentStart + contentStartTag.length, contentEnd)
- .replace(/^\n/, "")
- .replace(/\n$/, "")
- currentToolUse.params[contentParamName] = contentValue
- }
- }
-
- currentToolUse.partial = false // Mark as complete.
- contentBlocks.push(currentToolUse)
- currentToolUse = undefined // Reset state.
- currentTextContentStart = currentCharIndex + 1 // Potential text starts after this tag.
- continue // Move to next char.
- }
-
- // If not starting a param and not closing the tool, continue
- // accumulating tool content implicitly.
- continue
- }
-
- // Parsing text / looking for tool start.
- if (!currentToolUse) {
- // Check if starting a new tool use.
- let startedNewTool = false
-
- for (const [tag, toolName] of toolUseOpenTags.entries()) {
- if (
- currentCharIndex >= tag.length - 1 &&
- assistantMessage.startsWith(tag, currentCharIndex - tag.length + 1)
- ) {
- // End current text block if one was active.
- if (currentTextContent) {
- currentTextContent.content = assistantMessage
- .slice(
- currentTextContentStart, // From where text started.
- currentCharIndex - tag.length + 1, // To before the tool tag starts.
- )
- .trim()
-
- currentTextContent.partial = false // Ended because tool started.
-
- if (currentTextContent.content.length > 0) {
- contentBlocks.push(currentTextContent)
- }
-
- currentTextContent = undefined
- } else {
- // Check for any text between the last block and this tag.
- const potentialText = assistantMessage
- .slice(
- currentTextContentStart, // From where text *might* have started.
- currentCharIndex - tag.length + 1, // To before the tool tag starts.
- )
- .trim()
-
- if (potentialText.length > 0) {
- contentBlocks.push({
- type: "text",
- content: potentialText,
- partial: false,
- })
- }
- }
-
- // Start the new tool use.
- currentToolUse = {
- type: "tool_use",
- name: toolName,
- params: {},
- partial: true, // Assume partial until closing tag is found.
- }
-
- currentToolUseStart = currentCharIndex + 1 // Tool content starts after the opening tag.
- startedNewTool = true
-
- break
- }
- }
-
- if (startedNewTool) {
- continue // Handled start of tool, move to next char.
- }
-
- // If not starting a tool, it must be text content.
- if (!currentTextContent) {
- // Start a new text block if we aren't already in one.
- currentTextContentStart = currentCharIndex // Text starts at the current character.
-
- // Check if the current char is the start of potential text *immediately* after a tag.
- // This needs the previous state - simpler to let slicing handle it later.
- // Resetting start index accurately is key.
- // It should be the index *after* the last processed tag.
- // The logic managing currentTextContentStart after closing tags handles this.
- currentTextContent = {
- type: "text",
- content: "", // Will be determined by slicing at the end or when a tool starts
- partial: true,
- }
- }
- // Continue accumulating text implicitly; content is extracted later.
- }
- }
-
- // Finalize any open parameter within an open tool use.
- if (currentToolUse && currentParamName) {
- const value = assistantMessage.slice(currentParamValueStart) // From param start to end of string.
- // Don't trim content parameters to preserve newlines, but strip first and last newline only
- currentToolUse.params[currentParamName] =
- currentParamName === "content" ? value.replace(/^\n/, "").replace(/\n$/, "") : value.trim()
- // Tool use remains partial.
- }
-
- // Finalize any open tool use (which might contain the finalized partial param).
- if (currentToolUse) {
- // Tool use is partial because the loop finished before its closing tag.
- contentBlocks.push(currentToolUse)
- }
- // Finalize any trailing text content.
- // Only possible if a tool use wasn't open at the very end.
- else if (currentTextContent) {
- currentTextContent.content = assistantMessage
- .slice(currentTextContentStart) // From text start to end of string.
- .trim()
-
- // Text is partial because the loop finished.
- if (currentTextContent.content.length > 0) {
- contentBlocks.push(currentTextContent)
- }
- }
-
- return contentBlocks
-}
diff --git a/src/core/assistant-message/presentAssistantMessage.ts b/src/core/assistant-message/presentAssistantMessage.ts
index df97eccab7..24427f5618 100644
--- a/src/core/assistant-message/presentAssistantMessage.ts
+++ b/src/core/assistant-message/presentAssistantMessage.ts
@@ -18,7 +18,6 @@ import { Task } from "../task/Task"
import { fetchInstructionsTool } from "../tools/FetchInstructionsTool"
import { listFilesTool } from "../tools/ListFilesTool"
import { readFileTool } from "../tools/ReadFileTool"
-import { TOOL_PROTOCOL } from "@roo-code/types"
import { writeToFileTool } from "../tools/WriteToFileTool"
import { applyDiffTool } from "../tools/MultiApplyDiffTool"
import { searchAndReplaceTool } from "../tools/SearchAndReplaceTool"
@@ -38,7 +37,7 @@ import { updateTodoListTool } from "../tools/UpdateTodoListTool"
import { runSlashCommandTool } from "../tools/RunSlashCommandTool"
import { generateImageTool } from "../tools/GenerateImageTool"
import { applyDiffTool as applyDiffToolClass } from "../tools/ApplyDiffTool"
-import { validateToolUse } from "../tools/validateToolUse"
+import { isValidToolName, validateToolUse } from "../tools/validateToolUse"
import { codebaseSearchTool } from "../tools/CodebaseSearchTool"
import { formatResponse } from "../prompts/responses"
@@ -146,7 +145,6 @@ export async function presentAssistantMessage(cline: Task) {
// Track if we've already pushed a tool result
let hasToolResult = false
const toolCallId = mcpBlock.id
- const toolProtocol = TOOL_PROTOCOL.NATIVE // MCP tools in native mode always use native protocol
// Store approval feedback to merge into tool result (GitHub #10465)
let approvalFeedback: { text: string; images?: string[] } | undefined
@@ -174,7 +172,7 @@ export async function presentAssistantMessage(cline: Task) {
// Merge approval feedback into tool result (GitHub #10465)
if (approvalFeedback) {
- const feedbackText = formatResponse.toolApprovedWithFeedback(approvalFeedback.text, toolProtocol)
+ const feedbackText = formatResponse.toolApprovedWithFeedback(approvalFeedback.text)
resultContent = `${feedbackText}\n\n${resultContent}`
// Add feedback images to the image blocks
@@ -219,14 +217,9 @@ export async function presentAssistantMessage(cline: Task) {
if (response !== "yesButtonClicked") {
if (text) {
await cline.say("user_feedback", text, images)
- pushToolResult(
- formatResponse.toolResult(
- formatResponse.toolDeniedWithFeedback(text, toolProtocol),
- images,
- ),
- )
+ pushToolResult(formatResponse.toolResult(formatResponse.toolDeniedWithFeedback(text), images))
} else {
- pushToolResult(formatResponse.toolDenied(toolProtocol))
+ pushToolResult(formatResponse.toolDenied())
}
cline.didRejectTool = true
return false
@@ -254,12 +247,12 @@ export async function presentAssistantMessage(cline: Task) {
"error",
`Error ${action}:\n${error.message ?? JSON.stringify(serializeError(error), null, 2)}`,
)
- pushToolResult(formatResponse.toolError(errorString, toolProtocol))
+ pushToolResult(formatResponse.toolError(errorString))
}
if (!mcpBlock.partial) {
cline.recordToolUsage("use_mcp_tool") // Record as use_mcp_tool for analytics
- TelemetryService.instance.captureToolUsage(cline.taskId, "use_mcp_tool", toolProtocol)
+ TelemetryService.instance.captureToolUsage(cline.taskId, "use_mcp_tool")
}
// Resolve sanitized server name back to original server name
@@ -297,8 +290,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag: (tag, text) => text || "",
- toolProtocol,
})
break
}
@@ -313,58 +304,20 @@ export async function presentAssistantMessage(cline: Task) {
// Have to do this for partial and complete since sending
// content in thinking tags to markdown renderer will
// automatically be removed.
- // Remove end substrings of (with optional line break
- // after) and (with optional line break before).
- // - Needs to be separate since we dont want to remove the line
- // break before the first tag.
- // - Needs to happen before the xml parsing below.
+ // Strip any streamed tags from text output.
content = content.replace(/\s?/g, "")
content = content.replace(/\s?<\/thinking>/g, "")
- // Remove partial XML tag at the very end of the content (for
- // tool use and thinking tags), Prevents scrollview from
- // jumping when tags are automatically removed.
- const lastOpenBracketIndex = content.lastIndexOf("<")
-
- if (lastOpenBracketIndex !== -1) {
- const possibleTag = content.slice(lastOpenBracketIndex)
-
- // Check if there's a '>' after the last '<' (i.e., if the
- // tag is complete) (complete thinking and tool tags will
- // have been removed by now.)
- const hasCloseBracket = possibleTag.includes(">")
-
- if (!hasCloseBracket) {
- // Extract the potential tag name.
- let tagContent: string
-
- if (possibleTag.startsWith("")) {
- tagContent = possibleTag.slice(2).trim()
- } else {
- tagContent = possibleTag.slice(1).trim()
- }
-
- // Check if tagContent is likely an incomplete tag name
- // (letters and underscores only).
- const isLikelyTagName = /^[a-zA-Z_]+$/.test(tagContent)
-
- // Preemptively remove < or to keep from these
- // artifacts showing up in chat (also handles closing
- // thinking tags).
- const isOpeningOrClosing = possibleTag === "<" || possibleTag === ""
-
- // If the tag is incomplete and at the end, remove it
- // from the content.
- if (isOpeningOrClosing || isLikelyTagName) {
- content = content.slice(0, lastOpenBracketIndex).trim()
- }
- }
+ // Tool calling is native-only. If the model emits XML-style tool tags in a text block,
+ // fail fast with a clear error.
+ if (containsXmlToolMarkup(content)) {
+ const errorMessage =
+ "XML tool calls are no longer supported. Remove any XML tool markup (e.g. ...) and use native tool calling instead."
+ cline.consecutiveMistakeCount++
+ await cline.say("error", errorMessage)
+ cline.userMessageContent.push({ type: "text", text: errorMessage })
+ cline.didAlreadyUseTool = true
+ break
}
}
@@ -372,6 +325,30 @@ export async function presentAssistantMessage(cline: Task) {
break
}
case "tool_use": {
+ // Native tool calling is the only supported tool calling mechanism.
+ // A tool_use block without an id is invalid and cannot be executed.
+ const toolCallId = (block as any).id as string | undefined
+ if (!toolCallId) {
+ const errorMessage =
+ "Invalid tool call: missing tool_use.id. XML tool calls are no longer supported. Remove any XML tool markup (e.g. ...) and use native tool calling instead."
+ // Record a tool error for visibility/telemetry. Use the reported tool name if present.
+ try {
+ if (
+ typeof (cline as any).recordToolError === "function" &&
+ typeof (block as any).name === "string"
+ ) {
+ ;(cline as any).recordToolError((block as any).name as ToolName, errorMessage)
+ }
+ } catch {
+ // Best-effort only
+ }
+ cline.consecutiveMistakeCount++
+ await cline.say("error", errorMessage)
+ cline.userMessageContent.push({ type: "text", text: errorMessage })
+ cline.didAlreadyUseTool = true
+ break
+ }
+
// Fetch state early so it's available for toolDescription and validation
const state = await cline.providerRef.deref()?.getState()
const { mode, customModes, experiments: stateExperiments } = state ?? {}
@@ -392,24 +369,8 @@ export async function presentAssistantMessage(cline: Task) {
case "write_to_file":
return `[${block.name} for '${block.params.path}']`
case "apply_diff":
- // Handle both legacy format and new multi-file format
- if (block.params.path) {
- return `[${block.name} for '${block.params.path}']`
- } else if (block.params.args) {
- // Try to extract first file path from args for display
- const match = block.params.args.match(/.*?([^<]+)<\/path>/s)
- if (match) {
- const firstPath = match[1]
- // Check if there are multiple files
- const fileCount = (block.params.args.match(//g) || []).length
- if (fileCount > 1) {
- return `[${block.name} for '${firstPath}' and ${fileCount - 1} more file${fileCount > 2 ? "s" : ""}]`
- } else {
- return `[${block.name} for '${firstPath}']`
- }
- }
- }
- return `[${block.name}]`
+ // Native-only: tool args are structured (no XML payloads).
+ return block.params?.path ? `[${block.name} for '${block.params.path}']` : `[${block.name}]`
case "search_files":
return `[${block.name} for '${block.params.regex}'${
block.params.file_pattern ? ` in '${block.params.file_pattern}'` : ""
@@ -457,65 +418,73 @@ export async function presentAssistantMessage(cline: Task) {
if (cline.didRejectTool) {
// Ignore any tool content after user has rejected tool once.
- // For native protocol, we must send a tool_result for every tool_use to avoid API errors
- const toolCallId = block.id
+ // For native tool calling, we must send a tool_result for every tool_use to avoid API errors
const errorMessage = !block.partial
? `Skipping tool ${toolDescription()} due to user rejecting a previous tool.`
: `Tool ${toolDescription()} was interrupted and not executed due to user rejecting a previous tool.`
- if (toolCallId) {
- // Native protocol: MUST send tool_result for every tool_use
- cline.pushToolResultToUserContent({
- type: "tool_result",
- tool_use_id: toolCallId,
- content: errorMessage,
- is_error: true,
- })
- } else {
- // XML protocol: send as text
- cline.userMessageContent.push({
- type: "text",
- text: errorMessage,
- })
- }
+ cline.pushToolResultToUserContent({
+ type: "tool_result",
+ tool_use_id: toolCallId,
+ content: errorMessage,
+ is_error: true,
+ })
break
}
if (cline.didAlreadyUseTool) {
// Ignore any content after a tool has already been used.
- // For native protocol, we must send a tool_result for every tool_use to avoid API errors
- const toolCallId = block.id
+ // For native tool calling, we must send a tool_result for every tool_use to avoid API errors
const errorMessage = `Tool [${block.name}] was not executed because a tool has already been used in this message. Only one tool may be used per message. You must assess the first tool's result before proceeding to use the next tool.`
- if (toolCallId) {
- // Native protocol: MUST send tool_result for every tool_use
- cline.pushToolResultToUserContent({
- type: "tool_result",
- tool_use_id: toolCallId,
- content: errorMessage,
- is_error: true,
- })
- } else {
- // XML protocol: send as text
- cline.userMessageContent.push({
- type: "text",
- text: errorMessage,
- })
- }
+ cline.pushToolResultToUserContent({
+ type: "tool_result",
+ tool_use_id: toolCallId,
+ content: errorMessage,
+ is_error: true,
+ })
break
}
- // Track if we've already pushed a tool result for this tool call (native protocol only)
+ // Track if we've already pushed a tool result for this tool call (native tool calling only)
let hasToolResult = false
- // Determine protocol by checking if this tool call has an ID.
- // Native protocol tool calls ALWAYS have an ID (set when parsed from tool_call chunks).
- // XML protocol tool calls NEVER have an ID (parsed from XML text).
- const toolCallId = (block as any).id
- const toolProtocol = toolCallId ? TOOL_PROTOCOL.NATIVE : TOOL_PROTOCOL.XML
+ // If this is a native tool call but the parser couldn't construct nativeArgs
+ // (e.g., malformed/unfinished JSON in a streaming tool call), we must NOT attempt to
+ // execute the tool. Instead, emit exactly one structured tool_result so the provider
+ // receives a matching tool_result for the tool_use_id.
+ //
+ // This avoids executing an invalid tool_use block and prevents duplicate/fragmented
+ // error reporting.
+ if (!block.partial) {
+ const customTool = stateExperiments?.customTools ? customToolRegistry.get(block.name) : undefined
+ const isKnownTool = isValidToolName(String(block.name), stateExperiments)
+ if (isKnownTool && !block.nativeArgs && !customTool) {
+ const errorMessage =
+ `Invalid tool call for '${block.name}': missing nativeArgs. ` +
+ `This usually means the model streamed invalid or incomplete arguments and the call could not be finalized.`
+ cline.consecutiveMistakeCount++
+ try {
+ cline.recordToolError(block.name as ToolName, errorMessage)
+ } catch {
+ // Best-effort only
+ }
+
+ // Push tool_result directly without setting didAlreadyUseTool so streaming can
+ // continue gracefully.
+ cline.pushToolResultToUserContent({
+ type: "tool_result",
+ tool_use_id: toolCallId,
+ content: formatResponse.toolError(errorMessage),
+ is_error: true,
+ })
+
+ break
+ }
+ }
// Check experimental setting for multiple native tool calls
const isMultipleNativeToolCallsEnabled = experiments.isEnabled(
state?.experiments ?? {},
@@ -526,118 +495,49 @@ export async function presentAssistantMessage(cline: Task) {
let approvalFeedback: { text: string; images?: string[] } | undefined
const pushToolResult = (content: ToolResponse) => {
- if (toolProtocol === TOOL_PROTOCOL.NATIVE) {
- // For native protocol, only allow ONE tool_result per tool call
- if (hasToolResult) {
- console.warn(
- `[presentAssistantMessage] Skipping duplicate tool_result for tool_use_id: ${toolCallId}`,
- )
- return
- }
+ // Native tool calling: only allow ONE tool_result per tool call
+ if (hasToolResult) {
+ console.warn(
+ `[presentAssistantMessage] Skipping duplicate tool_result for tool_use_id: ${toolCallId}`,
+ )
+ return
+ }
- // For native protocol, tool_result content must be a string
- // Images are added as separate blocks in the user message
- let resultContent: string
- let imageBlocks: Anthropic.ImageBlockParam[] = []
+ let resultContent: string
+ let imageBlocks: Anthropic.ImageBlockParam[] = []
- if (typeof content === "string") {
- resultContent = content || "(tool did not return anything)"
- } else {
- // Separate text and image blocks
- const textBlocks = content.filter((item) => item.type === "text")
- imageBlocks = content.filter((item) => item.type === "image") as Anthropic.ImageBlockParam[]
-
- // Convert text blocks to string for tool_result
- resultContent =
- textBlocks.map((item) => (item as Anthropic.TextBlockParam).text).join("\n") ||
- "(tool did not return anything)"
- }
-
- // Merge approval feedback into tool result (GitHub #10465)
- if (approvalFeedback) {
- const feedbackText = formatResponse.toolApprovedWithFeedback(
- approvalFeedback.text,
- toolProtocol,
- )
- resultContent = `${feedbackText}\n\n${resultContent}`
-
- // Add feedback images to the image blocks
- if (approvalFeedback.images) {
- const feedbackImageBlocks = formatResponse.imageBlocks(approvalFeedback.images)
- imageBlocks = [...feedbackImageBlocks, ...imageBlocks]
- }
- }
-
- // Add tool_result with text content only
- cline.pushToolResultToUserContent({
- type: "tool_result",
- tool_use_id: toolCallId,
- content: resultContent,
- })
-
- // Add image blocks separately after tool_result
- if (imageBlocks.length > 0) {
- cline.userMessageContent.push(...imageBlocks)
- }
-
- hasToolResult = true
+ if (typeof content === "string") {
+ resultContent = content || "(tool did not return anything)"
} else {
- // For XML protocol, add as text blocks (legacy behavior)
- let resultContent: string
+ const textBlocks = content.filter((item) => item.type === "text")
+ imageBlocks = content.filter((item) => item.type === "image") as Anthropic.ImageBlockParam[]
+ resultContent =
+ textBlocks.map((item) => (item as Anthropic.TextBlockParam).text).join("\n") ||
+ "(tool did not return anything)"
+ }
- if (typeof content === "string") {
- resultContent = content || "(tool did not return anything)"
- } else {
- const textBlocks = content.filter((item) => item.type === "text")
- resultContent =
- textBlocks.map((item) => (item as Anthropic.TextBlockParam).text).join("\n") ||
- "(tool did not return anything)"
- }
-
- // Merge approval feedback into tool result (GitHub #10465)
- if (approvalFeedback) {
- const feedbackText = formatResponse.toolApprovedWithFeedback(
- approvalFeedback.text,
- toolProtocol,
- )
- resultContent = `${feedbackText}\n\n${resultContent}`
- }
-
- cline.userMessageContent.push({ type: "text", text: `${toolDescription()} Result:` })
-
- if (typeof content === "string") {
- cline.userMessageContent.push({
- type: "text",
- text: resultContent,
- })
- } else {
- // Add text content with merged feedback
- cline.userMessageContent.push({
- type: "text",
- text: resultContent,
- })
- // Add any images from the tool result
- const imageBlocks = content.filter((item) => item.type === "image")
- if (imageBlocks.length > 0) {
- cline.userMessageContent.push(...imageBlocks)
- }
+ // Merge approval feedback into tool result (GitHub #10465)
+ if (approvalFeedback) {
+ const feedbackText = formatResponse.toolApprovedWithFeedback(approvalFeedback.text)
+ resultContent = `${feedbackText}\n\n${resultContent}`
+ if (approvalFeedback.images) {
+ const feedbackImageBlocks = formatResponse.imageBlocks(approvalFeedback.images)
+ imageBlocks = [...feedbackImageBlocks, ...imageBlocks]
}
}
- // For XML protocol: Only one tool per message is allowed
- // For native protocol with experimental flag enabled: Multiple tools can be executed in sequence
- // For native protocol with experimental flag disabled: Single tool per message (default safe behavior)
- if (toolProtocol === TOOL_PROTOCOL.XML) {
- // Once a tool result has been collected, ignore all other tool
- // uses since we should only ever present one tool result per
- // message (XML protocol only).
- cline.didAlreadyUseTool = true
- } else if (toolProtocol === TOOL_PROTOCOL.NATIVE && !isMultipleNativeToolCallsEnabled) {
- // For native protocol with experimental flag disabled, enforce single tool per message
- cline.didAlreadyUseTool = true
+ cline.pushToolResultToUserContent({
+ type: "tool_result",
+ tool_use_id: toolCallId,
+ content: resultContent,
+ })
+
+ if (imageBlocks.length > 0) {
+ cline.userMessageContent.push(...imageBlocks)
}
- // If toolProtocol is NATIVE and isMultipleNativeToolCallsEnabled is true,
- // allow multiple tool calls in sequence (don't set didAlreadyUseTool)
+
+ hasToolResult = true
+ cline.didAlreadyUseTool = true
}
const askApproval = async (
@@ -658,14 +558,9 @@ export async function presentAssistantMessage(cline: Task) {
// Handle both messageResponse and noButtonClicked with text.
if (text) {
await cline.say("user_feedback", text, images)
- pushToolResult(
- formatResponse.toolResult(
- formatResponse.toolDeniedWithFeedback(text, toolProtocol),
- images,
- ),
- )
+ pushToolResult(formatResponse.toolResult(formatResponse.toolDeniedWithFeedback(text), images))
} else {
- pushToolResult(formatResponse.toolDenied(toolProtocol))
+ pushToolResult(formatResponse.toolDenied())
}
cline.didRejectTool = true
return false
@@ -704,34 +599,7 @@ export async function presentAssistantMessage(cline: Task) {
`Error ${action}:\n${error.message ?? JSON.stringify(serializeError(error), null, 2)}`,
)
- pushToolResult(formatResponse.toolError(errorString, toolProtocol))
- }
-
- // If block is partial, remove partial closing tag so its not
- // presented to user.
- const removeClosingTag = (tag: ToolParamName, text?: string): string => {
- if (!block.partial) {
- return text || ""
- }
-
- if (!text) {
- return ""
- }
-
- // This regex dynamically constructs a pattern to match the
- // closing tag:
- // - Optionally matches whitespace before the tag.
- // - Matches '<' or '' optionally followed by any subset of
- // characters from the tag name.
- const tagRegex = new RegExp(
- `\\s?<\/?${tag
- .split("")
- .map((char) => `(?:${char})?`)
- .join("")}$`,
- "g",
- )
-
- return text.replace(tagRegex, "")
+ pushToolResult(formatResponse.toolError(errorString))
}
// Keep browser open during an active session so other tools can run.
@@ -767,7 +635,7 @@ export async function presentAssistantMessage(cline: Task) {
const isCustomTool = stateExperiments?.customTools && customToolRegistry.has(block.name)
const recordName = isCustomTool ? "custom_tool" : block.name
cline.recordToolUsage(recordName)
- TelemetryService.instance.captureToolUsage(cline.taskId, recordName, toolProtocol)
+ TelemetryService.instance.captureToolUsage(cline.taskId, recordName)
}
// Validate tool use before execution - ONLY for complete (non-partial) blocks.
@@ -795,24 +663,18 @@ export async function presentAssistantMessage(cline: Task) {
} catch (error) {
cline.consecutiveMistakeCount++
// For validation errors (unknown tool, tool not allowed for mode), we need to:
- // 1. Send a tool_result with the error (required for native protocol)
+ // 1. Send a tool_result with the error (required for native tool calling)
// 2. NOT set didAlreadyUseTool = true (the tool was never executed, just failed validation)
// This prevents the stream from being interrupted with "Response interrupted by tool use result"
// which would cause the extension to appear to hang
- const errorContent = formatResponse.toolError(error.message, toolProtocol)
-
- if (toolProtocol === TOOL_PROTOCOL.NATIVE && toolCallId) {
- // For native protocol, push tool_result directly without setting didAlreadyUseTool
- cline.pushToolResultToUserContent({
- type: "tool_result",
- tool_use_id: toolCallId,
- content: typeof errorContent === "string" ? errorContent : "(validation error)",
- is_error: true,
- })
- } else {
- // For XML protocol, use the standard pushToolResult
- pushToolResult(errorContent)
- }
+ const errorContent = formatResponse.toolError(error.message)
+ // Push tool_result directly without setting didAlreadyUseTool
+ cline.pushToolResultToUserContent({
+ type: "tool_result",
+ tool_use_id: toolCallId,
+ content: typeof errorContent === "string" ? errorContent : "(validation error)",
+ is_error: true,
+ })
break
}
@@ -864,7 +726,6 @@ export async function presentAssistantMessage(cline: Task) {
pushToolResult(
formatResponse.toolError(
`Tool call repetition limit reached for ${block.name}. Please try a different approach.`,
- toolProtocol,
),
)
break
@@ -878,8 +739,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "update_todo_list":
@@ -887,26 +746,11 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "apply_diff": {
await checkpointSaveAndMark(cline)
- // Check if this tool call came from native protocol by checking for ID
- // Native calls always have IDs, XML calls never do
- if (toolProtocol === TOOL_PROTOCOL.NATIVE) {
- await applyDiffToolClass.handle(cline, block as ToolUse<"apply_diff">, {
- askApproval,
- handleError,
- pushToolResult,
- removeClosingTag,
- toolProtocol,
- })
- break
- }
-
// Get the provider and state to check experiment settings
const provider = cline.providerRef.deref()
let isMultiFileApplyDiffEnabled = false
@@ -920,14 +764,12 @@ export async function presentAssistantMessage(cline: Task) {
}
if (isMultiFileApplyDiffEnabled) {
- await applyDiffTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
+ await applyDiffTool(cline, block, askApproval, handleError, pushToolResult)
} else {
await applyDiffToolClass.handle(cline, block as ToolUse<"apply_diff">, {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
}
break
@@ -938,8 +780,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "search_replace":
@@ -948,8 +788,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "edit_file":
@@ -958,8 +796,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "apply_patch":
@@ -968,8 +804,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "read_file":
@@ -978,8 +812,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "fetch_instructions":
@@ -987,8 +819,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "list_files":
@@ -996,8 +826,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "codebase_search":
@@ -1005,8 +833,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "search_files":
@@ -1014,8 +840,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "browser_action":
@@ -1025,7 +849,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
)
break
case "execute_command":
@@ -1033,8 +856,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "use_mcp_tool":
@@ -1042,8 +863,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "access_mcp_resource":
@@ -1051,8 +870,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "ask_followup_question":
@@ -1060,8 +877,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "switch_mode":
@@ -1069,8 +884,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "new_task":
@@ -1078,8 +891,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
toolCallId: block.id,
})
break
@@ -1088,10 +899,8 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
askFinishSubTaskApproval,
toolDescription,
- toolProtocol,
}
await attemptCompletionTool.handle(
cline,
@@ -1105,8 +914,6 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
case "generate_image":
@@ -1115,13 +922,11 @@ export async function presentAssistantMessage(cline: Task) {
askApproval,
handleError,
pushToolResult,
- removeClosingTag,
- toolProtocol,
})
break
default: {
// Handle unknown/invalid tool names OR custom tools
- // This is critical for native protocol where every tool_use MUST have a tool_result
+ // This is critical for native tool calling where every tool_use MUST have a tool_result
// CRITICAL: Don't process partial blocks for unknown tools - just let them stream in.
// If we try to show errors for partial blocks, we'd show the error on every streaming chunk,
@@ -1144,7 +949,7 @@ export async function presentAssistantMessage(cline: Task) {
console.error(message)
cline.consecutiveMistakeCount++
await cline.say("error", message)
- pushToolResult(formatResponse.toolError(message, toolProtocol))
+ pushToolResult(formatResponse.toolError(message))
break
}
}
@@ -1175,18 +980,14 @@ export async function presentAssistantMessage(cline: Task) {
cline.consecutiveMistakeCount++
cline.recordToolError(block.name as ToolName, errorMessage)
await cline.say("error", t("tools:unknownToolError", { toolName: block.name }))
- // Push tool_result directly for native protocol WITHOUT setting didAlreadyUseTool
+ // Push tool_result directly WITHOUT setting didAlreadyUseTool
// This prevents the stream from being interrupted with "Response interrupted by tool use result"
- if (toolProtocol === TOOL_PROTOCOL.NATIVE && toolCallId) {
- cline.pushToolResultToUserContent({
- type: "tool_result",
- tool_use_id: toolCallId,
- content: formatResponse.toolError(errorMessage, toolProtocol),
- is_error: true,
- })
- } else {
- pushToolResult(formatResponse.toolError(errorMessage, toolProtocol))
- }
+ cline.pushToolResultToUserContent({
+ type: "tool_result",
+ tool_use_id: toolCallId,
+ content: formatResponse.toolError(errorMessage),
+ is_error: true,
+ })
break
}
}
@@ -1266,3 +1067,47 @@ async function checkpointSaveAndMark(task: Task) {
console.error(`[Task#presentAssistantMessage] Error saving checkpoint: ${error.message}`, error)
}
}
+
+function containsXmlToolMarkup(text: string): boolean {
+ // Keep this intentionally narrow: only reject XML-style tool tags matching our tool names.
+ // Avoid regex so we don't keep legacy XML parsing artifacts around.
+ // Note: This is a best-effort safeguard; tool_use blocks without an id are rejected elsewhere.
+
+ // First, strip out content inside markdown code fences to avoid false positives
+ // when users paste documentation or examples containing tool tag references.
+ // This handles both fenced code blocks (```) and inline code (`).
+ const textWithoutCodeBlocks = text
+ .replace(/```[\s\S]*?```/g, "") // Remove fenced code blocks
+ .replace(/`[^`]+`/g, "") // Remove inline code
+
+ const lower = textWithoutCodeBlocks.toLowerCase()
+ if (!lower.includes("<") || !lower.includes(">")) {
+ return false
+ }
+
+ const toolNames = [
+ "access_mcp_resource",
+ "apply_diff",
+ "apply_patch",
+ "ask_followup_question",
+ "attempt_completion",
+ "browser_action",
+ "codebase_search",
+ "edit_file",
+ "execute_command",
+ "fetch_instructions",
+ "generate_image",
+ "list_files",
+ "new_task",
+ "read_file",
+ "search_and_replace",
+ "search_files",
+ "search_replace",
+ "switch_mode",
+ "update_todo_list",
+ "use_mcp_tool",
+ "write_to_file",
+ ] as const
+
+ return toolNames.some((name) => lower.includes(`<${name}`) || lower.includes(`${name}`))
+}
diff --git a/src/core/assistant-message/types.ts b/src/core/assistant-message/types.ts
new file mode 100644
index 0000000000..7cd890cfdd
--- /dev/null
+++ b/src/core/assistant-message/types.ts
@@ -0,0 +1,3 @@
+import type { TextContent, ToolUse, McpToolUse } from "../../shared/tools"
+
+export type AssistantMessageContent = TextContent | ToolUse | McpToolUse
diff --git a/src/core/condense/__tests__/index.spec.ts b/src/core/condense/__tests__/index.spec.ts
index ef5af01243..2947d19ff1 100644
--- a/src/core/condense/__tests__/index.spec.ts
+++ b/src/core/condense/__tests__/index.spec.ts
@@ -1055,7 +1055,7 @@ describe("summarizeConversation", () => {
expect(mockApiHandler.createMessage).not.toHaveBeenCalled()
})
- it("should return error when both condensing and main API handlers are invalid", async () => {
+ it("should return error when API handler is invalid", async () => {
const messages: ApiMessage[] = [
{ role: "user", content: "Hello", ts: 1 },
{ role: "assistant", content: "Hi there", ts: 2 },
@@ -1066,14 +1066,8 @@ describe("summarizeConversation", () => {
{ role: "user", content: "Tell me more", ts: 7 },
]
- // Create invalid handlers (missing createMessage)
- const invalidMainHandler = {
- countTokens: vi.fn(),
- getModel: vi.fn(),
- // createMessage is missing
- } as unknown as ApiHandler
-
- const invalidCondensingHandler = {
+ // Create invalid handler (missing createMessage)
+ const invalidHandler = {
countTokens: vi.fn(),
getModel: vi.fn(),
// createMessage is missing
@@ -1086,16 +1080,13 @@ describe("summarizeConversation", () => {
const result = await summarizeConversation(
messages,
- invalidMainHandler,
+ invalidHandler,
defaultSystemPrompt,
taskId,
DEFAULT_PREV_CONTEXT_TOKENS,
- false,
- undefined,
- invalidCondensingHandler,
)
- // Should return original messages when both handlers are invalid
+ // Should return original messages when handler is invalid
expect(result.messages).toEqual(messages)
expect(result.cost).toBe(0)
expect(result.summary).toBe("")
@@ -1103,9 +1094,7 @@ describe("summarizeConversation", () => {
expect(result.newContextTokens).toBeUndefined()
// Verify error was logged
- expect(mockError).toHaveBeenCalledWith(
- expect.stringContaining("Main API handler is also invalid for condensing"),
- )
+ expect(mockError).toHaveBeenCalledWith(expect.stringContaining("API handler is invalid for condensing"))
// Restore console.error
console.error = originalError
@@ -1157,10 +1146,6 @@ describe("summarizeConversation", () => {
defaultSystemPrompt,
taskId,
DEFAULT_PREV_CONTEXT_TOKENS,
- false, // isAutomaticTrigger
- undefined, // customCondensingPrompt
- undefined, // condensingApiHandler
- true, // useNativeTools - required for tool_use block preservation
)
// Find the summary message
@@ -1236,10 +1221,6 @@ describe("summarizeConversation", () => {
defaultSystemPrompt,
taskId,
DEFAULT_PREV_CONTEXT_TOKENS,
- false,
- undefined,
- undefined,
- true,
)
expect(result.error).toBeUndefined()
@@ -1315,10 +1296,6 @@ describe("summarizeConversation", () => {
defaultSystemPrompt,
taskId,
DEFAULT_PREV_CONTEXT_TOKENS,
- false,
- undefined,
- undefined,
- true,
)
// Find the summary message (it has isSummary: true)
@@ -1389,10 +1366,6 @@ describe("summarizeConversation", () => {
defaultSystemPrompt,
taskId,
DEFAULT_PREV_CONTEXT_TOKENS,
- false, // isAutomaticTrigger
- undefined, // customCondensingPrompt
- undefined, // condensingApiHandler
- true, // useNativeTools - required for tool_use block preservation
)
// Find the summary message
@@ -1458,10 +1431,6 @@ describe("summarizeConversation", () => {
defaultSystemPrompt,
taskId,
DEFAULT_PREV_CONTEXT_TOKENS,
- false, // isAutomaticTrigger
- undefined, // customCondensingPrompt
- undefined, // condensingApiHandler
- false, // useNativeTools - not using tools in this test
)
// Find the summary message
@@ -1489,7 +1458,6 @@ describe("summarizeConversation", () => {
describe("summarizeConversation with custom settings", () => {
// Mock necessary dependencies
let mockMainApiHandler: ApiHandler
- let mockCondensingApiHandler: ApiHandler
const defaultSystemPrompt = "Default prompt"
const taskId = "test-task"
@@ -1511,7 +1479,7 @@ describe("summarizeConversation with custom settings", () => {
// Reset telemetry mock
;(TelemetryService.instance.captureContextCondensed as Mock).mockClear()
- // Setup mock API handlers
+ // Setup mock API handler
mockMainApiHandler = {
createMessage: vi.fn().mockImplementation(() => {
return (async function* () {
@@ -1534,29 +1502,6 @@ describe("summarizeConversation with custom settings", () => {
},
}),
} as unknown as ApiHandler
-
- mockCondensingApiHandler = {
- createMessage: vi.fn().mockImplementation(() => {
- return (async function* () {
- yield { type: "text" as const, text: "Summary from condensing handler" }
- yield { type: "usage" as const, totalCost: 0.03, outputTokens: 80 }
- })()
- }),
- countTokens: vi.fn().mockImplementation(() => Promise.resolve(40)),
- getModel: vi.fn().mockReturnValue({
- id: "condensing-model",
- info: {
- contextWindow: 4000,
- supportsImages: true,
- supportsVision: false,
- maxTokens: 2000,
- supportsPromptCache: false,
- maxCachePoints: 0,
- minTokensPerCachePoint: 0,
- cachableFields: [],
- },
- }),
- } as unknown as ApiHandler
})
/**
@@ -1619,84 +1564,6 @@ describe("summarizeConversation with custom settings", () => {
expect(createMessageCalls[0][0]).toContain("Your task is to create a detailed summary")
})
- /**
- * Test that condensing API handler is used when provided and valid
- */
- it("should use condensingApiHandler when provided and valid", async () => {
- await summarizeConversation(
- sampleMessages,
- mockMainApiHandler,
- defaultSystemPrompt,
- taskId,
- DEFAULT_PREV_CONTEXT_TOKENS,
- false,
- undefined,
- mockCondensingApiHandler,
- )
-
- // Verify the condensing handler was used
- expect((mockCondensingApiHandler.createMessage as Mock).mock.calls.length).toBe(1)
- expect((mockMainApiHandler.createMessage as Mock).mock.calls.length).toBe(0)
- })
-
- /**
- * Test fallback to main API handler when condensing handler is not provided
- */
- it("should fall back to mainApiHandler if condensingApiHandler is not provided", async () => {
- await summarizeConversation(
- sampleMessages,
- mockMainApiHandler,
- defaultSystemPrompt,
- taskId,
- DEFAULT_PREV_CONTEXT_TOKENS,
- false,
- undefined,
- undefined,
- )
-
- // Verify the main handler was used
- expect((mockMainApiHandler.createMessage as Mock).mock.calls.length).toBe(1)
- })
-
- /**
- * Test fallback to main API handler when condensing handler is invalid
- */
- it("should fall back to mainApiHandler if condensingApiHandler is invalid", async () => {
- // Create an invalid handler (missing createMessage)
- const invalidHandler = {
- countTokens: vi.fn(),
- getModel: vi.fn(),
- // createMessage is missing
- } as unknown as ApiHandler
-
- // Mock console.warn to verify warning message
- const originalWarn = console.warn
- const mockWarn = vi.fn()
- console.warn = mockWarn
-
- await summarizeConversation(
- sampleMessages,
- mockMainApiHandler,
- defaultSystemPrompt,
- taskId,
- DEFAULT_PREV_CONTEXT_TOKENS,
- false,
- undefined,
- invalidHandler,
- )
-
- // Verify the main handler was used as fallback
- expect((mockMainApiHandler.createMessage as Mock).mock.calls.length).toBe(1)
-
- // Verify warning was logged
- expect(mockWarn).toHaveBeenCalledWith(
- expect.stringContaining("Chosen API handler for condensing does not support message creation"),
- )
-
- // Restore console.warn
- console.warn = originalWarn
- })
-
/**
* Test that telemetry is called for custom prompt usage
*/
@@ -1716,38 +1583,13 @@ describe("summarizeConversation with custom settings", () => {
taskId,
false,
true, // usedCustomPrompt
- false, // usedCustomApiHandler
)
})
/**
- * Test that telemetry is called for custom API handler usage
+ * Test that telemetry is called with isAutomaticTrigger flag
*/
- it("should capture telemetry when using custom API handler", async () => {
- await summarizeConversation(
- sampleMessages,
- mockMainApiHandler,
- defaultSystemPrompt,
- taskId,
- DEFAULT_PREV_CONTEXT_TOKENS,
- false,
- undefined,
- mockCondensingApiHandler,
- )
-
- // Verify telemetry was called with custom API handler flag
- expect(TelemetryService.instance.captureContextCondensed).toHaveBeenCalledWith(
- taskId,
- false,
- false, // usedCustomPrompt
- true, // usedCustomApiHandler
- )
- })
-
- /**
- * Test that telemetry is called with both custom prompt and API handler
- */
- it("should capture telemetry when using both custom prompt and API handler", async () => {
+ it("should capture telemetry with isAutomaticTrigger flag", async () => {
await summarizeConversation(
sampleMessages,
mockMainApiHandler,
@@ -1756,15 +1598,13 @@ describe("summarizeConversation with custom settings", () => {
DEFAULT_PREV_CONTEXT_TOKENS,
true, // isAutomaticTrigger
"Custom prompt",
- mockCondensingApiHandler,
)
- // Verify telemetry was called with both flags
+ // Verify telemetry was called with isAutomaticTrigger flag
expect(TelemetryService.instance.captureContextCondensed).toHaveBeenCalledWith(
taskId,
true, // isAutomaticTrigger
true, // usedCustomPrompt
- true, // usedCustomApiHandler
)
})
})
diff --git a/src/core/condense/index.ts b/src/core/condense/index.ts
index 79bc31ef9f..9c4617c7a6 100644
--- a/src/core/condense/index.ts
+++ b/src/core/condense/index.ts
@@ -8,11 +8,11 @@ import { ApiHandler } from "../../api"
import { ApiMessage } from "../task-persistence/apiMessages"
import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning"
import { findLast } from "../../shared/array"
+import { supportPrompt } from "../../shared/support-prompt"
/**
* Checks if a message contains tool_result blocks.
- * For native tools protocol, user messages with tool_result blocks require
- * corresponding tool_use blocks from the previous assistant turn.
+ * User messages with tool_result blocks require corresponding tool_use blocks from the previous assistant turn.
*/
function hasToolResultBlocks(message: ApiMessage): boolean {
if (message.role !== "user" || typeof message.content === "string") {
@@ -154,45 +154,7 @@ export const N_MESSAGES_TO_KEEP = 3
export const MIN_CONDENSE_THRESHOLD = 5 // Minimum percentage of context window to trigger condensing
export const MAX_CONDENSE_THRESHOLD = 100 // Maximum percentage of context window to trigger condensing
-const SUMMARY_PROMPT = `\
-Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions.
-This summary should be thorough in capturing technical details, code patterns, and architectural decisions that would be essential for continuing with the conversation and supporting any continuing tasks.
-
-Your summary should be structured as follows:
-Context: The context to continue the conversation with. If applicable based on the current task, this should include:
- 1. Previous Conversation: High level details about what was discussed throughout the entire conversation with the user. This should be written to allow someone to be able to follow the general overarching conversation flow.
- 2. Current Work: Describe in detail what was being worked on prior to this request to summarize the conversation. Pay special attention to the more recent messages in the conversation.
- 3. Key Technical Concepts: List all important technical concepts, technologies, coding conventions, and frameworks discussed, which might be relevant for continuing with this work.
- 4. Relevant Files and Code: If applicable, enumerate specific files and code sections examined, modified, or created for the task continuation. Pay special attention to the most recent messages and changes.
- 5. Problem Solving: Document problems solved thus far and any ongoing troubleshooting efforts.
- 6. Pending Tasks and Next Steps: Outline all pending tasks that you have explicitly been asked to work on, as well as list the next steps you will take for all outstanding work, if applicable. Include code snippets where they add clarity. For any next steps, include direct quotes from the most recent conversation showing exactly what task you were working on and where you left off. This should be verbatim to ensure there's no information loss in context between tasks.
-
-Example summary structure:
-1. Previous Conversation:
- [Detailed description]
-2. Current Work:
- [Detailed description]
-3. Key Technical Concepts:
- - [Concept 1]
- - [Concept 2]
- - [...]
-4. Relevant Files and Code:
- - [File Name 1]
- - [Summary of why this file is important]
- - [Summary of the changes made to this file, if any]
- - [Important Code Snippet]
- - [File Name 2]
- - [Important Code Snippet]
- - [...]
-5. Problem Solving:
- [Detailed description]
-6. Pending Tasks and Next Steps:
- - [Task 1 details & next steps]
- - [Task 2 details & next steps]
- - [...]
-
-Output only the summary of the conversation so far, without any additional commentary or explanation.
-`
+const SUMMARY_PROMPT = supportPrompt.default.CONDENSE
export type SummarizeResponse = {
messages: ApiMessage[] // The messages after summarization
@@ -207,24 +169,12 @@ export type SummarizeResponse = {
* Summarizes the conversation messages using an LLM call
*
* @param {ApiMessage[]} messages - The conversation messages
- * @param {ApiHandler} apiHandler - The API handler to use for token counting.
- * @param {string} systemPrompt - The system prompt for API requests, which should be considered in the context token count
- * @param {string} taskId - The task ID for the conversation, used for telemetry
- * @param {boolean} isAutomaticTrigger - Whether the summarization is triggered automatically
- * @returns {SummarizeResponse} - The result of the summarization operation (see above)
- */
-/**
- * Summarizes the conversation messages using an LLM call
- *
- * @param {ApiMessage[]} messages - The conversation messages
- * @param {ApiHandler} apiHandler - The API handler to use for token counting (fallback if condensingApiHandler not provided)
+ * @param {ApiHandler} apiHandler - The API handler to use for summarization and token counting
* @param {string} systemPrompt - The system prompt for API requests (fallback if customCondensingPrompt not provided)
* @param {string} taskId - The task ID for the conversation, used for telemetry
* @param {number} prevContextTokens - The number of tokens currently in the context, used to ensure we don't grow the context
* @param {boolean} isAutomaticTrigger - Whether the summarization is triggered automatically
* @param {string} customCondensingPrompt - Optional custom prompt to use for condensing
- * @param {ApiHandler} condensingApiHandler - Optional specific API handler to use for condensing
- * @param {boolean} useNativeTools - Whether native tools protocol is being used (requires tool_use/tool_result pairing)
* @returns {SummarizeResponse} - The result of the summarization operation (see above)
*/
export async function summarizeConversation(
@@ -235,14 +185,11 @@ export async function summarizeConversation(
prevContextTokens: number,
isAutomaticTrigger?: boolean,
customCondensingPrompt?: string,
- condensingApiHandler?: ApiHandler,
- useNativeTools?: boolean,
): Promise {
TelemetryService.instance.captureContextCondensed(
taskId,
isAutomaticTrigger ?? false,
!!customCondensingPrompt?.trim(),
- !!condensingApiHandler,
)
const response: SummarizeResponse = { messages, cost: 0, summary: "" }
@@ -250,15 +197,11 @@ export async function summarizeConversation(
// Always preserve the first message (which may contain slash command content)
const firstMessage = messages[0]
- // Get keepMessages and any tool_use/reasoning blocks that need to be preserved for tool_result pairing
- // Only preserve these blocks when using native tools protocol (XML protocol doesn't need them)
- const { keepMessages, toolUseBlocksToPreserve, reasoningBlocksToPreserve } = useNativeTools
- ? getKeepMessagesWithToolBlocks(messages, N_MESSAGES_TO_KEEP)
- : {
- keepMessages: messages.slice(-N_MESSAGES_TO_KEEP),
- toolUseBlocksToPreserve: [],
- reasoningBlocksToPreserve: [],
- }
+ // Get keepMessages and any tool_use/reasoning blocks that need to be preserved for tool_result pairing.
+ const { keepMessages, toolUseBlocksToPreserve, reasoningBlocksToPreserve } = getKeepMessagesWithToolBlocks(
+ messages,
+ N_MESSAGES_TO_KEEP,
+ )
const keepStartIndex = Math.max(messages.length - N_MESSAGES_TO_KEEP, 0)
const includeFirstKeptMessageInSummary = toolUseBlocksToPreserve.length > 0
@@ -297,29 +240,14 @@ export async function summarizeConversation(
// Use custom prompt if provided and non-empty, otherwise use the default SUMMARY_PROMPT
const promptToUse = customCondensingPrompt?.trim() ? customCondensingPrompt.trim() : SUMMARY_PROMPT
- // Use condensing API handler if provided, otherwise use main API handler
- let handlerToUse = condensingApiHandler || apiHandler
-
- // Check if the chosen handler supports the required functionality
- if (!handlerToUse || typeof handlerToUse.createMessage !== "function") {
- console.warn(
- "Chosen API handler for condensing does not support message creation or is invalid, falling back to main apiHandler.",
- )
-
- handlerToUse = apiHandler // Fallback to the main, presumably valid, apiHandler
-
- // Ensure the main apiHandler itself is valid before this point or add another check.
- if (!handlerToUse || typeof handlerToUse.createMessage !== "function") {
- // This case should ideally not happen if main apiHandler is always valid.
- // Consider throwing an error or returning a specific error response.
- console.error("Main API handler is also invalid for condensing. Cannot proceed.")
- // Return an appropriate error structure for SummarizeResponse
- const error = t("common:errors.condense_handler_invalid")
- return { ...response, error }
- }
+ // Validate that the API handler supports message creation
+ if (!apiHandler || typeof apiHandler.createMessage !== "function") {
+ console.error("API handler is invalid for condensing. Cannot proceed.")
+ const error = t("common:errors.condense_handler_invalid")
+ return { ...response, error }
}
- const stream = handlerToUse.createMessage(promptToUse, requestMessages)
+ const stream = apiHandler.createMessage(promptToUse, requestMessages)
let summary = ""
let cost = 0
diff --git a/src/core/config/ContextProxy.ts b/src/core/config/ContextProxy.ts
index 64baf546bd..c3b602ea74 100644
--- a/src/core/config/ContextProxy.ts
+++ b/src/core/config/ContextProxy.ts
@@ -20,6 +20,7 @@ import {
import { TelemetryService } from "@roo-code/telemetry"
import { logger } from "../../utils/logging"
+import { supportPrompt } from "../../shared/support-prompt"
type GlobalStateKey = keyof GlobalState
type SecretStateKey = keyof SecretState
@@ -92,9 +93,51 @@ export class ContextProxy {
// Migration: Sanitize invalid/removed API providers
await this.migrateInvalidApiProvider()
+ // Migration: Move legacy customCondensingPrompt to customSupportPrompts
+ await this.migrateLegacyCondensingPrompt()
+
this._isInitialized = true
}
+ /**
+ * Migrates the legacy customCondensingPrompt to the new customSupportPrompts structure
+ * and removes the legacy field.
+ *
+ * Note: Only true customizations are migrated. If the legacy prompt equals the default,
+ * we skip the migration to avoid pinning users to an old default if the default changes.
+ */
+ private async migrateLegacyCondensingPrompt() {
+ try {
+ const legacyPrompt = this.originalContext.globalState.get("customCondensingPrompt")
+ if (legacyPrompt) {
+ const currentSupportPrompts =
+ this.originalContext.globalState.get>("customSupportPrompts") || {}
+
+ // Only migrate if:
+ // 1. The new location doesn't already have a value
+ // 2. The legacy prompt is a true customization (not equal to the default)
+ // This prevents pinning users to an old default if the default prompt changes.
+ const isCustomized = legacyPrompt.trim() !== supportPrompt.default.CONDENSE.trim()
+ if (!currentSupportPrompts.CONDENSE && isCustomized) {
+ logger.info("Migrating customized legacy customCondensingPrompt to customSupportPrompts")
+ const updatedPrompts = { ...currentSupportPrompts, CONDENSE: legacyPrompt }
+ await this.originalContext.globalState.update("customSupportPrompts", updatedPrompts)
+ this.stateCache.customSupportPrompts = updatedPrompts
+ } else if (!isCustomized) {
+ logger.info("Skipping migration: legacy customCondensingPrompt equals the default prompt")
+ }
+
+ // Always remove the legacy field
+ await this.originalContext.globalState.update("customCondensingPrompt", undefined)
+ this.stateCache.customCondensingPrompt = undefined
+ }
+ } catch (error) {
+ logger.error(
+ `Error during customCondensingPrompt migration: ${error instanceof Error ? error.message : String(error)}`,
+ )
+ }
+ }
+
/**
* Migrates invalid/removed apiProvider values by clearing them from storage.
* This handles cases where a user had a provider selected that was later removed
diff --git a/src/core/config/ProviderSettingsManager.ts b/src/core/config/ProviderSettingsManager.ts
index 420ab332b2..bf145f09c2 100644
--- a/src/core/config/ProviderSettingsManager.ts
+++ b/src/core/config/ProviderSettingsManager.ts
@@ -183,7 +183,8 @@ export class ProviderSettingsManager {
if (!providerProfiles.migrations.claudeCodeLegacySettingsMigrated) {
// These keys were used by the removed local Claude Code CLI wrapper.
for (const apiConfig of Object.values(providerProfiles.apiConfigs)) {
- if (apiConfig.apiProvider !== "claude-code") continue
+ // Cast to string for comparison since "claude-code" is no longer a valid ProviderName
+ if ((apiConfig.apiProvider as string) !== "claude-code") continue
const config = apiConfig as unknown as Record
if ("claudeCodePath" in config) {
diff --git a/src/core/config/__tests__/ContextProxy.spec.ts b/src/core/config/__tests__/ContextProxy.spec.ts
index 49e706b181..bfdbd1619f 100644
--- a/src/core/config/__tests__/ContextProxy.spec.ts
+++ b/src/core/config/__tests__/ContextProxy.spec.ts
@@ -70,13 +70,16 @@ describe("ContextProxy", () => {
describe("constructor", () => {
it("should initialize state cache with all global state keys", () => {
- // +1 for the migration check of old nested settings
- expect(mockGlobalState.get).toHaveBeenCalledTimes(GLOBAL_STATE_KEYS.length + 1)
+ // +2 for the migration checks:
+ // 1. openRouterImageGenerationSettings
+ // 2. customCondensingPrompt
+ expect(mockGlobalState.get).toHaveBeenCalledTimes(GLOBAL_STATE_KEYS.length + 2)
for (const key of GLOBAL_STATE_KEYS) {
expect(mockGlobalState.get).toHaveBeenCalledWith(key)
}
- // Also check for migration call
+ // Also check for migration calls
expect(mockGlobalState.get).toHaveBeenCalledWith("openRouterImageGenerationSettings")
+ expect(mockGlobalState.get).toHaveBeenCalledWith("customCondensingPrompt")
})
it("should initialize secret cache with all secret keys", () => {
@@ -99,8 +102,8 @@ describe("ContextProxy", () => {
const result = proxy.getGlobalState("apiProvider")
expect(result).toBe("deepseek")
- // Original context should be called once during updateGlobalState (+1 for migration check)
- expect(mockGlobalState.get).toHaveBeenCalledTimes(GLOBAL_STATE_KEYS.length + 1) // From initialization + migration check
+ // Original context should be called once during updateGlobalState (+2 for migration checks)
+ expect(mockGlobalState.get).toHaveBeenCalledTimes(GLOBAL_STATE_KEYS.length + 2) // From initialization + migration checks
})
it("should handle default values correctly", async () => {
diff --git a/src/core/config/__tests__/importExport.spec.ts b/src/core/config/__tests__/importExport.spec.ts
index 3d5329f377..9aee8693c6 100644
--- a/src/core/config/__tests__/importExport.spec.ts
+++ b/src/core/config/__tests__/importExport.spec.ts
@@ -68,15 +68,6 @@ vi.mock("../../../api", () => ({
buildApiHandler: vi.fn().mockImplementation((config) => {
// Return different model info based on the provider and model
const getModelInfo = () => {
- if (config.apiProvider === "claude-code") {
- return {
- id: config.apiModelId || "claude-sonnet-4-5",
- info: {
- supportsReasoningBudget: false,
- requiredReasoningBudget: false,
- },
- }
- }
if (config.apiProvider === "anthropic" && config.apiModelId === "claude-3-5-sonnet-20241022") {
return {
id: "claude-3-5-sonnet-20241022",
@@ -126,6 +117,7 @@ describe("importExport", () => {
setValue: vi.fn(),
export: vi.fn().mockImplementation(() => Promise.resolve({})),
setProviderSettings: vi.fn(),
+ getValue: vi.fn(),
} as unknown as ReturnType>
mockCustomModesManager = { updateCustomMode: vi.fn() } as unknown as ReturnType<
@@ -458,6 +450,7 @@ describe("importExport", () => {
const mockProvider = {
settingsImportedAt: 0,
postStateToWebview: vi.fn().mockResolvedValue(undefined),
+ postStateToWebviewWithoutTaskHistory: vi.fn().mockResolvedValue(undefined),
}
// Mock the showErrorMessage to capture the error
@@ -483,18 +476,17 @@ describe("importExport", () => {
it("should handle import when reasoning budget fields are missing from config", async () => {
// This test verifies that import works correctly when reasoning budget fields are not present
- // Using claude-code provider which doesn't support reasoning budgets
;(vscode.window.showOpenDialog as Mock).mockResolvedValue([{ fsPath: "/mock/path/settings.json" }])
const mockFileContent = JSON.stringify({
providerProfiles: {
- currentApiConfigName: "claude-code-provider",
+ currentApiConfigName: "openai-provider",
apiConfigs: {
- "claude-code-provider": {
- apiProvider: "claude-code" as ProviderName,
- apiModelId: "claude-3-5-sonnet-20241022",
- id: "claude-code-id",
+ "openai-provider": {
+ apiProvider: "openai" as ProviderName,
+ apiModelId: "gpt-4",
+ id: "openai-id",
apiKey: "test-key",
// No modelMaxTokens or modelMaxThinkingTokens fields
},
@@ -512,7 +504,7 @@ describe("importExport", () => {
mockProviderSettingsManager.export.mockResolvedValue(previousProviderProfiles)
mockProviderSettingsManager.listConfig.mockResolvedValue([
- { name: "claude-code-provider", id: "claude-code-id", apiProvider: "claude-code" as ProviderName },
+ { name: "openai-provider", id: "openai-id", apiProvider: "openai" as ProviderName },
{ name: "default", id: "default-id", apiProvider: "anthropic" as ProviderName },
])
@@ -529,21 +521,21 @@ describe("importExport", () => {
expect(mockProviderSettingsManager.export).toHaveBeenCalled()
expect(mockProviderSettingsManager.import).toHaveBeenCalledWith({
- currentApiConfigName: "claude-code-provider",
+ currentApiConfigName: "openai-provider",
apiConfigs: {
default: { apiProvider: "anthropic" as ProviderName, id: "default-id" },
- "claude-code-provider": {
- apiProvider: "claude-code" as ProviderName,
- apiModelId: "claude-3-5-sonnet-20241022",
+ "openai-provider": {
+ apiProvider: "openai" as ProviderName,
+ apiModelId: "gpt-4",
apiKey: "test-key",
- id: "claude-code-id",
+ id: "openai-id",
},
},
modeApiConfigs: {},
})
expect(mockContextProxy.setValues).toHaveBeenCalledWith({ mode: "code", autoApprovalEnabled: true })
- expect(mockContextProxy.setValue).toHaveBeenCalledWith("currentApiConfigName", "claude-code-provider")
+ expect(mockContextProxy.setValue).toHaveBeenCalledWith("currentApiConfigName", "openai-provider")
})
})
@@ -702,7 +694,7 @@ describe("importExport", () => {
defaultUri: expect.anything(),
})
- expect(vscode.Uri.file).toHaveBeenCalledWith(path.join("/mock/home", "Documents", "roo-code-settings.json"))
+ expect(vscode.Uri.file).toHaveBeenCalledWith(path.join("/mock/home", "Downloads", "roo-code-settings.json"))
})
describe("codebase indexing export", () => {
@@ -1721,27 +1713,27 @@ describe("importExport", () => {
it.each([
{
testCase: "supportsReasoningBudget is false",
- providerName: "claude-code-provider",
- modelId: "claude-sonnet-4-5",
- providerId: "claude-code-id",
+ providerName: "deepseek-provider",
+ modelId: "deepseek-chat",
+ providerId: "deepseek-id",
},
{
testCase: "requiredReasoningBudget is false",
- providerName: "claude-code-provider-2",
- modelId: "claude-sonnet-4-5",
- providerId: "claude-code-id-2",
+ providerName: "deepseek-provider-2",
+ modelId: "deepseek-coder",
+ providerId: "deepseek-id-2",
},
{
testCase: "both supportsReasoningBudget and requiredReasoningBudget are false",
- providerName: "claude-code-provider-3",
- modelId: "claude-3-5-haiku-20241022",
- providerId: "claude-code-id-3",
+ providerName: "deepseek-provider-3",
+ modelId: "deepseek-reasoner",
+ providerId: "deepseek-id-3",
},
])(
"should exclude modelMaxTokens and modelMaxThinkingTokens when $testCase",
async ({ providerName, modelId, providerId }) => {
// This test verifies that token fields are excluded when model doesn't support reasoning budget
- // Using claude-code provider which has supportsReasoningBudget: false and requiredReasoningBudget: false
+ // Using deepseek provider which uses apiModelId and has supportsReasoningBudget: false
;(vscode.window.showSaveDialog as Mock).mockResolvedValue({
fsPath: "/mock/path/roo-code-settings.json",
@@ -1753,12 +1745,12 @@ describe("importExport", () => {
// Wait for initialization to complete
await realProviderSettingsManager.initialize()
- // Save a claude-code provider config with token fields
+ // Save a deepseek provider config with token fields
await realProviderSettingsManager.saveConfig(providerName, {
- apiProvider: "claude-code" as ProviderName,
+ apiProvider: "deepseek" as ProviderName,
apiModelId: modelId,
id: providerId,
- apiKey: "test-key",
+ deepSeekApiKey: "test-key",
modelMaxTokens: 4096, // This should be removed during export
modelMaxThinkingTokens: 2048, // This should be removed during export
})
diff --git a/src/core/config/importExport.ts b/src/core/config/importExport.ts
index c3d6f9c215..de3119e0c9 100644
--- a/src/core/config/importExport.ts
+++ b/src/core/config/importExport.ts
@@ -12,6 +12,7 @@ import { TelemetryService } from "@roo-code/telemetry"
import { ProviderSettingsManager, providerProfilesSchema } from "./ProviderSettingsManager"
import { ContextProxy } from "./ContextProxy"
import { CustomModesManager } from "./CustomModesManager"
+import { resolveDefaultSaveUri, saveLastExportPath } from "../../utils/export"
import { t } from "../../i18n"
export type ImportOptions = {
@@ -143,15 +144,22 @@ export const importSettingsFromFile = async (
}
export const exportSettings = async ({ providerSettingsManager, contextProxy }: ExportOptions) => {
+ const defaultUri = await resolveDefaultSaveUri(contextProxy, "lastSettingsExportPath", "roo-code-settings.json", {
+ useWorkspace: false,
+ fallbackDir: path.join(os.homedir(), "Downloads"),
+ })
+
const uri = await vscode.window.showSaveDialog({
filters: { JSON: ["json"] },
- defaultUri: vscode.Uri.file(path.join(os.homedir(), "Documents", "roo-code-settings.json")),
+ defaultUri,
})
if (!uri) {
return
}
+ await saveLastExportPath(contextProxy, "lastSettingsExportPath", uri)
+
try {
const providerProfiles = await providerSettingsManager.export()
const globalSettings = await contextProxy.export()
diff --git a/src/core/context-management/__tests__/context-management.spec.ts b/src/core/context-management/__tests__/context-management.spec.ts
index 3ee36fc595..cbd4c6b795 100644
--- a/src/core/context-management/__tests__/context-management.spec.ts
+++ b/src/core/context-management/__tests__/context-management.spec.ts
@@ -620,8 +620,6 @@ describe("Context Management", () => {
70001,
true,
undefined, // customCondensingPrompt
- undefined, // condensingApiHandler
- undefined, // useNativeTools
)
// Verify the result contains the summary information
@@ -796,8 +794,6 @@ describe("Context Management", () => {
60000,
true,
undefined, // customCondensingPrompt
- undefined, // condensingApiHandler
- undefined, // useNativeTools
)
// Verify the result contains the summary information
diff --git a/src/core/context-management/index.ts b/src/core/context-management/index.ts
index a94a53c9d5..78be5d404d 100644
--- a/src/core/context-management/index.ts
+++ b/src/core/context-management/index.ts
@@ -216,10 +216,8 @@ export type ContextManagementOptions = {
systemPrompt: string
taskId: string
customCondensingPrompt?: string
- condensingApiHandler?: ApiHandler
profileThresholds: Record
currentProfileId: string
- useNativeTools?: boolean
}
export type ContextManagementResult = SummarizeResponse & {
@@ -246,10 +244,8 @@ export async function manageContext({
systemPrompt,
taskId,
customCondensingPrompt,
- condensingApiHandler,
profileThresholds,
currentProfileId,
- useNativeTools,
}: ContextManagementOptions): Promise {
let error: string | undefined
let cost = 0
@@ -302,8 +298,6 @@ export async function manageContext({
prevContextTokens,
true, // automatic trigger
customCondensingPrompt,
- condensingApiHandler,
- useNativeTools,
)
if (result.error) {
error = result.error
diff --git a/src/core/diff/strategies/multi-file-search-replace.ts b/src/core/diff/strategies/multi-file-search-replace.ts
index 1236a98fbb..f8cd1e522b 100644
--- a/src/core/diff/strategies/multi-file-search-replace.ts
+++ b/src/core/diff/strategies/multi-file-search-replace.ts
@@ -202,7 +202,6 @@ def greet(name):
-
Usage:
diff --git a/src/core/diff/strategies/multi-search-replace.ts b/src/core/diff/strategies/multi-search-replace.ts
index a6a9913203..739eb20faf 100644
--- a/src/core/diff/strategies/multi-search-replace.ts
+++ b/src/core/diff/strategies/multi-search-replace.ts
@@ -115,7 +115,6 @@ Diff format:
\`\`\`
-
Example:
Original file:
@@ -168,7 +167,6 @@ def calculate_sum(items):
>>>>>>> REPLACE
\`\`\`
-
Usage:
File path here
diff --git a/src/core/environment/getEnvironmentDetails.ts b/src/core/environment/getEnvironmentDetails.ts
index ebb6f18e48..4460f34f1c 100644
--- a/src/core/environment/getEnvironmentDetails.ts
+++ b/src/core/environment/getEnvironmentDetails.ts
@@ -8,7 +8,6 @@ import delay from "delay"
import type { ExperimentId } from "@roo-code/types"
import { DEFAULT_TERMINAL_OUTPUT_CHARACTER_LIMIT } from "@roo-code/types"
-import { resolveToolProtocol } from "../../utils/resolveToolProtocol"
import { EXPERIMENT_IDS, experiments as Experiments } from "../../shared/experiments"
import { formatLanguage } from "../../shared/language"
import { defaultModeSlug, getFullModeDetails } from "../../shared/modes"
@@ -236,18 +235,13 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo
language: language ?? formatLanguage(vscode.env.language),
})
- // 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, cline.taskToolProtocol)
+ const toolFormat = "native"
details += `\n\n# Current Mode\n`
details += `${currentMode}\n`
details += `${modeDetails.name}\n`
details += `${modelId}\n`
- details += `${toolProtocol}\n`
+ details += `${toolFormat}\n`
if (Experiments.isEnabled(experiments ?? {}, EXPERIMENT_IDS.POWER_STEERING)) {
details += `${modeDetails.roleDefinition}\n`
diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap
index ee8a50e993..70cccc68f0 100644
--- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap
+++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap
@@ -10,351 +10,19 @@ ALL responses MUST show ANY `language construct` OR filename reference as clicka
TOOL USE
-You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
+You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples. You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response.
-# Tool Use Formatting
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.
-
-# Tools
-
-## read_file
-Description: Request to read the contents of one or more files. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code. Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
-
-**IMPORTANT: You can read a maximum of 5 files in a single request.** If you need to read more files, use multiple sequential read_file requests.
-
-
-Parameters:
-- args: Contains one or more file elements, where each file contains:
- - path: (required) File path (relative to workspace directory /test/path)
-
-
-Usage:
-
-
-
- path/to/file
-
-
-
-
-
-Examples:
-
-1. Reading a single file:
-
-
-
- src/app.ts
-
-
-
-
-
-2. Reading multiple files (within the 5-file limit):
-
-
-
- src/app.ts
-
-
-
- src/utils.ts
-
-
-
-
-
-3. Reading an entire file:
-
-
-
- config.json
-
-
-
-
-IMPORTANT: You MUST use this Efficient Reading Strategy:
-- You MUST read all related files and implementations together in a single operation (up to 5 files at once)
-- You MUST obtain all necessary context before proceeding with changes
-
-- When you need to read more than 5 files, prioritize the most critical files first, then use subsequent read_file requests for additional files
-
-## fetch_instructions
-Description: Request to fetch instructions to perform a task
-Parameters:
-- task: (required) The task to get instructions for. This can take the following values:
- create_mcp_server
- create_mode
-
-Example: Requesting instructions to create an MCP Server
-
-
-create_mcp_server
-
-
-## search_files
-Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
-
-Craft your regex patterns carefully to balance specificity and flexibility. Use this tool to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include surrounding context, so analyze the surrounding code to better understand the matches. Leverage this 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.
-
-Parameters:
-- path: (required) The path of the directory to search in (relative to the current workspace directory /test/path). This directory will be recursively searched.
-- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
-- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
-
-Usage:
-
-Directory path here
-Your regex pattern here
-file pattern here (optional)
-
-
-Example: Searching for all .ts files in the current directory
-
-.
-.*
-*.ts
-
-
-Example: Searching for function definitions in JavaScript files
-
-src
-function\s+\w+
-*.js
-
-
-## list_files
-Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
-Parameters:
-- path: (required) The path of the directory to list contents for (relative to the current workspace directory /test/path)
-- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
-Usage:
-
-Directory path here
-true or false (optional)
-
-
-Example: Requesting to list all files in the current directory
-
-.
-false
-
-
-## write_to_file
-Description: Request to write content to a file. This tool is primarily used for **creating new files** or for scenarios where a **complete rewrite of an existing file is intentionally required**. If the file exists, it will be overwritten. If it doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
-
-**Important:** You should prefer using other editing tools over write_to_file when making changes to existing files, since write_to_file is slower and cannot handle large files. Use write_to_file primarily for new file creation.
-
-When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code.
-
-When creating a new project, organize all new files within a dedicated project directory unless the user specifies otherwise. Structure the project logically, adhering to best practices for the specific type of project being created.
-
-Parameters:
-- path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
-- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.
-
-Usage:
-
-File path here
-
-Your file content here
-
-
-
-Example: Writing a configuration file
-
-frontend-config.json
-
-{
- "apiEndpoint": "https://api.example.com",
- "theme": {
- "primaryColor": "#007bff",
- "secondaryColor": "#6c757d",
- "fontFamily": "Arial, sans-serif"
- },
- "features": {
- "darkMode": true,
- "notifications": true,
- "analytics": false
- },
- "version": "1.0.0"
-}
-
-
-
-## ask_followup_question
-Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively.
-
-Parameters:
-- question: (required) A clear, specific question addressing the information needed
-- follow_up: (required) A list of 2-4 suggested answers, each in its own tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
-
-Usage:
-
-Your question here
-
-First suggestion
-Action with mode switch
-
-
-
-Example:
-
-What is the path to the frontend-config.json file?
-
-./src/frontend-config.json
-./config/frontend-config.json
-./frontend-config.json
-
-
-
-## attempt_completion
-Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
-IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.
-Parameters:
-- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.
-Usage:
-
-
-Your final result description here
-
-
-
-Example: Requesting to attempt completion with a result
-
-
-I've updated the CSS
-
-
-
-## switch_mode
-Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch.
-Parameters:
-- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect")
-- reason: (optional) The reason for switching modes
-Usage:
-
-Mode slug here
-Reason for switching here
-
-
-Example: Requesting to switch to code mode
-
-code
-Need to make code changes
-
-
-## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-
-Example:
-
-code
-Implement a new feature for the application
-
-
-
-## update_todo_list
-
-**Description:**
-Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks.
-
-**Checklist Format:**
-- Use a single-level markdown checklist (no nesting or subtasks).
-- List todos in the intended execution order.
-- Status options:
- - [ ] Task description (pending)
- - [x] Task description (completed)
- - [-] Task description (in progress)
-
-**Status Rules:**
-- [ ] = pending (not started)
-- [x] = completed (fully finished, no unresolved issues)
-- [-] = in_progress (currently being worked on)
-
-**Core Principles:**
-- Before updating, always confirm which todos have been completed since the last update.
-- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress).
-- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately.
-- Do not remove any unfinished todos unless explicitly instructed.
-- Always retain all unfinished tasks, updating their status as needed.
-- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies).
-- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved.
-- Remove tasks only if they are no longer relevant or if the user requests deletion.
-
-**Usage Example:**
-
-
-[x] Analyze requirements
-[x] Design architecture
-[-] Implement core logic
-[ ] Write tests
-[ ] Update documentation
-
-
-
-*After completing "Implement core logic" and starting "Write tests":*
-
-
-[x] Analyze requirements
-[x] Design architecture
-[x] Implement core logic
-[-] Write tests
-[ ] Update documentation
-[ ] Add performance benchmarks
-
-
-
-**When to Use:**
-- The task is complicated or involves multiple steps or requires ongoing tracking.
-- You need to update the status of several todos at once.
-- New actionable items are discovered during task execution.
-- The user requests a todo list or provides multiple tasks.
-- The task is complex and benefits from clear, stepwise progress tracking.
-
-**When NOT to Use:**
-- There is only a single, trivial task.
-- The task can be completed in one or two simple steps.
-- The request is purely conversational or informational.
-
-**Task Management Guidelines:**
-- Mark task as completed immediately after all work of the current task is done.
-- Start the next task by marking it as in_progress.
-- Add new todos as soon as they are identified.
-- Use clear, descriptive task names.
-
-
-# Tool Use Guidelines
+ # Tool Use Guidelines
1. Assess what information you already have and what information you need to proceed with the task.
2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like `ls` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.
3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.
-4. Formulate your tool use using the XML format specified for each tool.
-5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
+4. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
- Information about whether the tool succeeded or failed, along with any reasons for failure.
- Linter errors that may have arisen due to the changes you made, which you'll need to address.
- New terminal output in reaction to the changes, which you may need to consider or act upon.
- Any other relevant feedback or information related to the tool use.
-6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
+5. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
It is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:
1. Confirm the success of each step before proceeding.
@@ -385,7 +53,7 @@ MODES
RULES
- The project base directory is: /test/path
-- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to .
+- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to execute_command.
- You cannot `cd` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path.
- Do not use the ~ character or $HOME to refer to the home directory.
- 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 '/test/path', and if so prepend with `cd`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run `npm install` in a project outside of '/test/path', 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)`.
diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/ask-mode-prompt.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/ask-mode-prompt.snap
index 4428748632..ee604b3036 100644
--- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/ask-mode-prompt.snap
+++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/ask-mode-prompt.snap
@@ -10,309 +10,19 @@ ALL responses MUST show ANY `language construct` OR filename reference as clicka
TOOL USE
-You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
+You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples. You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response.
-# Tool Use Formatting
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.
-
-# Tools
-
-## read_file
-Description: Request to read the contents of one or more files. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code. Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
-
-**IMPORTANT: You can read a maximum of 5 files in a single request.** If you need to read more files, use multiple sequential read_file requests.
-
-
-Parameters:
-- args: Contains one or more file elements, where each file contains:
- - path: (required) File path (relative to workspace directory /test/path)
-
-
-Usage:
-
-
-
- path/to/file
-
-
-
-
-
-Examples:
-
-1. Reading a single file:
-
-
-
- src/app.ts
-
-
-
-
-
-2. Reading multiple files (within the 5-file limit):
-
-
-
- src/app.ts
-
-
-
- src/utils.ts
-
-
-
-
-
-3. Reading an entire file:
-
-
-
- config.json
-
-
-
-
-IMPORTANT: You MUST use this Efficient Reading Strategy:
-- You MUST read all related files and implementations together in a single operation (up to 5 files at once)
-- You MUST obtain all necessary context before proceeding with changes
-
-- When you need to read more than 5 files, prioritize the most critical files first, then use subsequent read_file requests for additional files
-
-## fetch_instructions
-Description: Request to fetch instructions to perform a task
-Parameters:
-- task: (required) The task to get instructions for. This can take the following values:
- create_mcp_server
- create_mode
-
-Example: Requesting instructions to create an MCP Server
-
-
-create_mcp_server
-
-
-## search_files
-Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
-
-Craft your regex patterns carefully to balance specificity and flexibility. Use this tool to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include surrounding context, so analyze the surrounding code to better understand the matches. Leverage this 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.
-
-Parameters:
-- path: (required) The path of the directory to search in (relative to the current workspace directory /test/path). This directory will be recursively searched.
-- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
-- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
-
-Usage:
-
-Directory path here
-Your regex pattern here
-file pattern here (optional)
-
-
-Example: Searching for all .ts files in the current directory
-
-.
-.*
-*.ts
-
-
-Example: Searching for function definitions in JavaScript files
-
-src
-function\s+\w+
-*.js
-
-
-## list_files
-Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
-Parameters:
-- path: (required) The path of the directory to list contents for (relative to the current workspace directory /test/path)
-- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
-Usage:
-
-Directory path here
-true or false (optional)
-
-
-Example: Requesting to list all files in the current directory
-
-.
-false
-
-
-## ask_followup_question
-Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively.
-
-Parameters:
-- question: (required) A clear, specific question addressing the information needed
-- follow_up: (required) A list of 2-4 suggested answers, each in its own tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
-
-Usage:
-
-Your question here
-
-First suggestion
-Action with mode switch
-
-
-
-Example:
-
-What is the path to the frontend-config.json file?
-
-./src/frontend-config.json
-./config/frontend-config.json
-./frontend-config.json
-
-
-
-## attempt_completion
-Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
-IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.
-Parameters:
-- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.
-Usage:
-
-
-Your final result description here
-
-
-
-Example: Requesting to attempt completion with a result
-
-
-I've updated the CSS
-
-
-
-## switch_mode
-Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch.
-Parameters:
-- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect")
-- reason: (optional) The reason for switching modes
-Usage:
-
-Mode slug here
-Reason for switching here
-
-
-Example: Requesting to switch to code mode
-
-code
-Need to make code changes
-
-
-## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-
-Example:
-
-code
-Implement a new feature for the application
-
-
-
-## update_todo_list
-
-**Description:**
-Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks.
-
-**Checklist Format:**
-- Use a single-level markdown checklist (no nesting or subtasks).
-- List todos in the intended execution order.
-- Status options:
- - [ ] Task description (pending)
- - [x] Task description (completed)
- - [-] Task description (in progress)
-
-**Status Rules:**
-- [ ] = pending (not started)
-- [x] = completed (fully finished, no unresolved issues)
-- [-] = in_progress (currently being worked on)
-
-**Core Principles:**
-- Before updating, always confirm which todos have been completed since the last update.
-- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress).
-- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately.
-- Do not remove any unfinished todos unless explicitly instructed.
-- Always retain all unfinished tasks, updating their status as needed.
-- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies).
-- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved.
-- Remove tasks only if they are no longer relevant or if the user requests deletion.
-
-**Usage Example:**
-
-
-[x] Analyze requirements
-[x] Design architecture
-[-] Implement core logic
-[ ] Write tests
-[ ] Update documentation
-
-
-
-*After completing "Implement core logic" and starting "Write tests":*
-
-
-[x] Analyze requirements
-[x] Design architecture
-[x] Implement core logic
-[-] Write tests
-[ ] Update documentation
-[ ] Add performance benchmarks
-
-
-
-**When to Use:**
-- The task is complicated or involves multiple steps or requires ongoing tracking.
-- You need to update the status of several todos at once.
-- New actionable items are discovered during task execution.
-- The user requests a todo list or provides multiple tasks.
-- The task is complex and benefits from clear, stepwise progress tracking.
-
-**When NOT to Use:**
-- There is only a single, trivial task.
-- The task can be completed in one or two simple steps.
-- The request is purely conversational or informational.
-
-**Task Management Guidelines:**
-- Mark task as completed immediately after all work of the current task is done.
-- Start the next task by marking it as in_progress.
-- Add new todos as soon as they are identified.
-- Use clear, descriptive task names.
-
-
-# Tool Use Guidelines
+ # Tool Use Guidelines
1. Assess what information you already have and what information you need to proceed with the task.
2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like `ls` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.
3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.
-4. Formulate your tool use using the XML format specified for each tool.
-5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
+4. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
- Information about whether the tool succeeded or failed, along with any reasons for failure.
- Linter errors that may have arisen due to the changes you made, which you'll need to address.
- New terminal output in reaction to the changes, which you may need to consider or act upon.
- Any other relevant feedback or information related to the tool use.
-6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
+5. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
It is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:
1. Confirm the success of each step before proceeding.
@@ -343,7 +53,7 @@ MODES
RULES
- The project base directory is: /test/path
-- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to .
+- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to execute_command.
- You cannot `cd` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path.
- Do not use the ~ character or $HOME to refer to the home directory.
- 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 '/test/path', and if so prepend with `cd`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run `npm install` in a project outside of '/test/path', 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)`.
diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap
index 48b39d001f..70cccc68f0 100644
--- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap
+++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap
@@ -10,350 +10,19 @@ ALL responses MUST show ANY `language construct` OR filename reference as clicka
TOOL USE
-You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
+You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples. You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response.
-# Tool Use Formatting
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.
-
-# Tools
-
-## read_file
-Description: Request to read the contents of one or more files. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code. Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
-
-**IMPORTANT: You can read a maximum of 5 files in a single request.** If you need to read more files, use multiple sequential read_file requests.
-
-
-Parameters:
-- args: Contains one or more file elements, where each file contains:
- - path: (required) File path (relative to workspace directory /test/path)
-
-
-Usage:
-
-
-
- path/to/file
-
-
-
-
-
-Examples:
-
-1. Reading a single file:
-
-
-
- src/app.ts
-
-
-
-
-
-2. Reading multiple files (within the 5-file limit):
-
-
-
- src/app.ts
-
-
-
- src/utils.ts
-
-
-
-
-
-3. Reading an entire file:
-
-
-
- config.json
-
-
-
-
-IMPORTANT: You MUST use this Efficient Reading Strategy:
-- You MUST read all related files and implementations together in a single operation (up to 5 files at once)
-- You MUST obtain all necessary context before proceeding with changes
-
-- When you need to read more than 5 files, prioritize the most critical files first, then use subsequent read_file requests for additional files
-
-## fetch_instructions
-Description: Request to fetch instructions to perform a task
-Parameters:
-- task: (required) The task to get instructions for. This can take the following values:
- create_mode
-
-Example: Requesting instructions to create a Mode
-
-
-create_mode
-
-
-## search_files
-Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
-
-Craft your regex patterns carefully to balance specificity and flexibility. Use this tool to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include surrounding context, so analyze the surrounding code to better understand the matches. Leverage this 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.
-
-Parameters:
-- path: (required) The path of the directory to search in (relative to the current workspace directory /test/path). This directory will be recursively searched.
-- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
-- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
-
-Usage:
-
-Directory path here
-Your regex pattern here
-file pattern here (optional)
-
-
-Example: Searching for all .ts files in the current directory
-
-.
-.*
-*.ts
-
-
-Example: Searching for function definitions in JavaScript files
-
-src
-function\s+\w+
-*.js
-
-
-## list_files
-Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
-Parameters:
-- path: (required) The path of the directory to list contents for (relative to the current workspace directory /test/path)
-- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
-Usage:
-
-Directory path here
-true or false (optional)
-
-
-Example: Requesting to list all files in the current directory
-
-.
-false
-
-
-## write_to_file
-Description: Request to write content to a file. This tool is primarily used for **creating new files** or for scenarios where a **complete rewrite of an existing file is intentionally required**. If the file exists, it will be overwritten. If it doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
-
-**Important:** You should prefer using other editing tools over write_to_file when making changes to existing files, since write_to_file is slower and cannot handle large files. Use write_to_file primarily for new file creation.
-
-When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code.
-
-When creating a new project, organize all new files within a dedicated project directory unless the user specifies otherwise. Structure the project logically, adhering to best practices for the specific type of project being created.
-
-Parameters:
-- path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
-- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.
-
-Usage:
-
-File path here
-
-Your file content here
-
-
-
-Example: Writing a configuration file
-
-frontend-config.json
-
-{
- "apiEndpoint": "https://api.example.com",
- "theme": {
- "primaryColor": "#007bff",
- "secondaryColor": "#6c757d",
- "fontFamily": "Arial, sans-serif"
- },
- "features": {
- "darkMode": true,
- "notifications": true,
- "analytics": false
- },
- "version": "1.0.0"
-}
-
-
-
-## ask_followup_question
-Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively.
-
-Parameters:
-- question: (required) A clear, specific question addressing the information needed
-- follow_up: (required) A list of 2-4 suggested answers, each in its own tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
-
-Usage:
-
-Your question here
-
-First suggestion
-Action with mode switch
-
-
-
-Example:
-
-What is the path to the frontend-config.json file?
-
-./src/frontend-config.json
-./config/frontend-config.json
-./frontend-config.json
-
-
-
-## attempt_completion
-Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
-IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.
-Parameters:
-- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.
-Usage:
-
-
-Your final result description here
-
-
-
-Example: Requesting to attempt completion with a result
-
-
-I've updated the CSS
-
-
-
-## switch_mode
-Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch.
-Parameters:
-- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect")
-- reason: (optional) The reason for switching modes
-Usage:
-
-Mode slug here
-Reason for switching here
-
-
-Example: Requesting to switch to code mode
-
-code
-Need to make code changes
-
-
-## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-
-Example:
-
-code
-Implement a new feature for the application
-
-
-
-## update_todo_list
-
-**Description:**
-Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks.
-
-**Checklist Format:**
-- Use a single-level markdown checklist (no nesting or subtasks).
-- List todos in the intended execution order.
-- Status options:
- - [ ] Task description (pending)
- - [x] Task description (completed)
- - [-] Task description (in progress)
-
-**Status Rules:**
-- [ ] = pending (not started)
-- [x] = completed (fully finished, no unresolved issues)
-- [-] = in_progress (currently being worked on)
-
-**Core Principles:**
-- Before updating, always confirm which todos have been completed since the last update.
-- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress).
-- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately.
-- Do not remove any unfinished todos unless explicitly instructed.
-- Always retain all unfinished tasks, updating their status as needed.
-- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies).
-- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved.
-- Remove tasks only if they are no longer relevant or if the user requests deletion.
-
-**Usage Example:**
-
-
-[x] Analyze requirements
-[x] Design architecture
-[-] Implement core logic
-[ ] Write tests
-[ ] Update documentation
-
-
-
-*After completing "Implement core logic" and starting "Write tests":*
-
-
-[x] Analyze requirements
-[x] Design architecture
-[x] Implement core logic
-[-] Write tests
-[ ] Update documentation
-[ ] Add performance benchmarks
-
-
-
-**When to Use:**
-- The task is complicated or involves multiple steps or requires ongoing tracking.
-- You need to update the status of several todos at once.
-- New actionable items are discovered during task execution.
-- The user requests a todo list or provides multiple tasks.
-- The task is complex and benefits from clear, stepwise progress tracking.
-
-**When NOT to Use:**
-- There is only a single, trivial task.
-- The task can be completed in one or two simple steps.
-- The request is purely conversational or informational.
-
-**Task Management Guidelines:**
-- Mark task as completed immediately after all work of the current task is done.
-- Start the next task by marking it as in_progress.
-- Add new todos as soon as they are identified.
-- Use clear, descriptive task names.
-
-
-# Tool Use Guidelines
+ # Tool Use Guidelines
1. Assess what information you already have and what information you need to proceed with the task.
2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like `ls` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.
3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.
-4. Formulate your tool use using the XML format specified for each tool.
-5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
+4. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
- Information about whether the tool succeeded or failed, along with any reasons for failure.
- Linter errors that may have arisen due to the changes you made, which you'll need to address.
- New terminal output in reaction to the changes, which you may need to consider or act upon.
- Any other relevant feedback or information related to the tool use.
-6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
+5. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
It is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:
1. Confirm the success of each step before proceeding.
@@ -384,7 +53,7 @@ MODES
RULES
- The project base directory is: /test/path
-- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to .
+- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to execute_command.
- You cannot `cd` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path.
- Do not use the ~ character or $HOME to refer to the home directory.
- 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 '/test/path', and if so prepend with `cd`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run `npm install` in a project outside of '/test/path', 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)`.
diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-enabled.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-enabled.snap
index acc36d1ffd..51fd18172b 100644
--- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-enabled.snap
+++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-enabled.snap
@@ -10,400 +10,19 @@ ALL responses MUST show ANY `language construct` OR filename reference as clicka
TOOL USE
-You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
+You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples. You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response.
-# Tool Use Formatting
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.
-
-# Tools
-
-## read_file
-Description: Request to read the contents of one or more files. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code. Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
-
-**IMPORTANT: You can read a maximum of 5 files in a single request.** If you need to read more files, use multiple sequential read_file requests.
-
-
-Parameters:
-- args: Contains one or more file elements, where each file contains:
- - path: (required) File path (relative to workspace directory /test/path)
-
-
-Usage:
-
-
-
- path/to/file
-
-
-
-
-
-Examples:
-
-1. Reading a single file:
-
-
-
- src/app.ts
-
-
-
-
-
-2. Reading multiple files (within the 5-file limit):
-
-
-
- src/app.ts
-
-
-
- src/utils.ts
-
-
-
-
-
-3. Reading an entire file:
-
-
-
- config.json
-
-
-
-
-IMPORTANT: You MUST use this Efficient Reading Strategy:
-- You MUST read all related files and implementations together in a single operation (up to 5 files at once)
-- You MUST obtain all necessary context before proceeding with changes
-
-- When you need to read more than 5 files, prioritize the most critical files first, then use subsequent read_file requests for additional files
-
-## fetch_instructions
-Description: Request to fetch instructions to perform a task
-Parameters:
-- task: (required) The task to get instructions for. This can take the following values:
- create_mcp_server
- create_mode
-
-Example: Requesting instructions to create an MCP Server
-
-
-create_mcp_server
-
-
-## search_files
-Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
-
-Craft your regex patterns carefully to balance specificity and flexibility. Use this tool to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include surrounding context, so analyze the surrounding code to better understand the matches. Leverage this 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.
-
-Parameters:
-- path: (required) The path of the directory to search in (relative to the current workspace directory /test/path). This directory will be recursively searched.
-- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
-- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
-
-Usage:
-
-Directory path here
-Your regex pattern here
-file pattern here (optional)
-
-
-Example: Searching for all .ts files in the current directory
-
-.
-.*
-*.ts
-
-
-Example: Searching for function definitions in JavaScript files
-
-src
-function\s+\w+
-*.js
-
-
-## list_files
-Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
-Parameters:
-- path: (required) The path of the directory to list contents for (relative to the current workspace directory /test/path)
-- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
-Usage:
-
-Directory path here
-true or false (optional)
-
-
-Example: Requesting to list all files in the current directory
-
-.
-false
-
-
-## write_to_file
-Description: Request to write content to a file. This tool is primarily used for **creating new files** or for scenarios where a **complete rewrite of an existing file is intentionally required**. If the file exists, it will be overwritten. If it doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
-
-**Important:** You should prefer using other editing tools over write_to_file when making changes to existing files, since write_to_file is slower and cannot handle large files. Use write_to_file primarily for new file creation.
-
-When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code.
-
-When creating a new project, organize all new files within a dedicated project directory unless the user specifies otherwise. Structure the project logically, adhering to best practices for the specific type of project being created.
-
-Parameters:
-- path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
-- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.
-
-Usage:
-
-File path here
-
-Your file content here
-
-
-
-Example: Writing a configuration file
-
-frontend-config.json
-
-{
- "apiEndpoint": "https://api.example.com",
- "theme": {
- "primaryColor": "#007bff",
- "secondaryColor": "#6c757d",
- "fontFamily": "Arial, sans-serif"
- },
- "features": {
- "darkMode": true,
- "notifications": true,
- "analytics": false
- },
- "version": "1.0.0"
-}
-
-
-
-## use_mcp_tool
-Description: Request to use a tool provided by a connected MCP server. Each MCP server can provide multiple tools with different capabilities. Tools have defined input schemas that specify required and optional parameters.
-Parameters:
-- server_name: (required) The name of the MCP server providing the tool
-- tool_name: (required) The name of the tool to execute
-- arguments: (required) A JSON object containing the tool's input parameters, following the tool's input schema
-Usage:
-
-server name here
-tool name here
-
-{
- "param1": "value1",
- "param2": "value2"
-}
-
-
-
-Example: Requesting to use an MCP tool
-
-
-weather-server
-get_forecast
-
-{
- "city": "San Francisco",
- "days": 5
-}
-
-
-
-## access_mcp_resource
-Description: Request to access a resource provided by a connected MCP server. Resources represent data sources that can be used as context, such as files, API responses, or system information.
-Parameters:
-- server_name: (required) The name of the MCP server providing the resource
-- uri: (required) The URI identifying the specific resource to access
-Usage:
-
-server name here
-resource URI here
-
-
-Example: Requesting to access an MCP resource
-
-
-weather-server
-weather://san-francisco/current
-
-
-## ask_followup_question
-Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively.
-
-Parameters:
-- question: (required) A clear, specific question addressing the information needed
-- follow_up: (required) A list of 2-4 suggested answers, each in its own tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
-
-Usage:
-
-Your question here
-
-First suggestion
-Action with mode switch
-
-
-
-Example:
-
-What is the path to the frontend-config.json file?
-
-./src/frontend-config.json
-./config/frontend-config.json
-./frontend-config.json
-
-
-
-## attempt_completion
-Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
-IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.
-Parameters:
-- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.
-Usage:
-
-
-Your final result description here
-
-
-
-Example: Requesting to attempt completion with a result
-
-
-I've updated the CSS
-
-
-
-## switch_mode
-Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch.
-Parameters:
-- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect")
-- reason: (optional) The reason for switching modes
-Usage:
-
-Mode slug here
-Reason for switching here
-
-
-Example: Requesting to switch to code mode
-
-code
-Need to make code changes
-
-
-## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-
-Example:
-
-code
-Implement a new feature for the application
-
-
-
-## update_todo_list
-
-**Description:**
-Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks.
-
-**Checklist Format:**
-- Use a single-level markdown checklist (no nesting or subtasks).
-- List todos in the intended execution order.
-- Status options:
- - [ ] Task description (pending)
- - [x] Task description (completed)
- - [-] Task description (in progress)
-
-**Status Rules:**
-- [ ] = pending (not started)
-- [x] = completed (fully finished, no unresolved issues)
-- [-] = in_progress (currently being worked on)
-
-**Core Principles:**
-- Before updating, always confirm which todos have been completed since the last update.
-- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress).
-- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately.
-- Do not remove any unfinished todos unless explicitly instructed.
-- Always retain all unfinished tasks, updating their status as needed.
-- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies).
-- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved.
-- Remove tasks only if they are no longer relevant or if the user requests deletion.
-
-**Usage Example:**
-
-
-[x] Analyze requirements
-[x] Design architecture
-[-] Implement core logic
-[ ] Write tests
-[ ] Update documentation
-
-
-
-*After completing "Implement core logic" and starting "Write tests":*
-
-
-[x] Analyze requirements
-[x] Design architecture
-[x] Implement core logic
-[-] Write tests
-[ ] Update documentation
-[ ] Add performance benchmarks
-
-
-
-**When to Use:**
-- The task is complicated or involves multiple steps or requires ongoing tracking.
-- You need to update the status of several todos at once.
-- New actionable items are discovered during task execution.
-- The user requests a todo list or provides multiple tasks.
-- The task is complex and benefits from clear, stepwise progress tracking.
-
-**When NOT to Use:**
-- There is only a single, trivial task.
-- The task can be completed in one or two simple steps.
-- The request is purely conversational or informational.
-
-**Task Management Guidelines:**
-- Mark task as completed immediately after all work of the current task is done.
-- Start the next task by marking it as in_progress.
-- Add new todos as soon as they are identified.
-- Use clear, descriptive task names.
-
-
-# Tool Use Guidelines
+ # Tool Use Guidelines
1. Assess what information you already have and what information you need to proceed with the task.
2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like `ls` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.
3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.
-4. Formulate your tool use using the XML format specified for each tool.
-5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
+4. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
- Information about whether the tool succeeded or failed, along with any reasons for failure.
- Linter errors that may have arisen due to the changes you made, which you'll need to address.
- New terminal output in reaction to the changes, which you may need to consider or act upon.
- Any other relevant feedback or information related to the tool use.
-6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
+5. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
It is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:
1. Confirm the success of each step before proceeding.
@@ -453,7 +72,7 @@ MODES
RULES
- The project base directory is: /test/path
-- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to .
+- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to execute_command.
- You cannot `cd` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path.
- Do not use the ~ character or $HOME to refer to the home directory.
- 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 '/test/path', and if so prepend with `cd`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run `npm install` in a project outside of '/test/path', 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)`.
diff --git a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/partial-reads-enabled.snap b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/partial-reads-enabled.snap
index ac93623fda..70cccc68f0 100644
--- a/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/partial-reads-enabled.snap
+++ b/src/core/prompts/__tests__/__snapshots__/add-custom-instructions/partial-reads-enabled.snap
@@ -10,356 +10,19 @@ ALL responses MUST show ANY `language construct` OR filename reference as clicka
TOOL USE
-You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
+You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples. You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response.
-# Tool Use Formatting
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.
-
-# Tools
-
-## read_file
-Description: Request to read the contents of one or more files. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code. Use line ranges to efficiently read specific portions of large files. Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
-
-**IMPORTANT: You can read a maximum of 5 files in a single request.** If you need to read more files, use multiple sequential read_file requests.
-
-By specifying line ranges, you can efficiently read specific portions of large files without loading the entire file into memory.
-Parameters:
-- args: Contains one or more file elements, where each file contains:
- - path: (required) File path (relative to workspace directory /test/path)
- - line_range: (optional) One or more line range elements in format "start-end" (1-based, inclusive)
-
-Usage:
-
-
-
- path/to/file
- start-end
-
-
-
-
-Examples:
-
-1. Reading a single file:
-
-
-
- src/app.ts
- 1-1000
-
-
-
-
-2. Reading multiple files (within the 5-file limit):
-
-
-
- src/app.ts
- 1-50
- 100-150
-
-
- src/utils.ts
- 10-20
-
-
-
-
-3. Reading an entire file:
-
-
-
- config.json
-
-
-
-
-IMPORTANT: You MUST use this Efficient Reading Strategy:
-- You MUST read all related files and implementations together in a single operation (up to 5 files at once)
-- You MUST obtain all necessary context before proceeding with changes
-- You MUST use line ranges to read specific portions of large files, rather than reading entire files when not needed
-- You MUST combine adjacent line ranges (<10 lines apart)
-- You MUST use multiple ranges for content separated by >10 lines
-- You MUST include sufficient line context for planned modifications while keeping ranges minimal
-
-- When you need to read more than 5 files, prioritize the most critical files first, then use subsequent read_file requests for additional files
-
-## fetch_instructions
-Description: Request to fetch instructions to perform a task
-Parameters:
-- task: (required) The task to get instructions for. This can take the following values:
- create_mcp_server
- create_mode
-
-Example: Requesting instructions to create an MCP Server
-
-
-create_mcp_server
-
-
-## search_files
-Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
-
-Craft your regex patterns carefully to balance specificity and flexibility. Use this tool to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include surrounding context, so analyze the surrounding code to better understand the matches. Leverage this 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.
-
-Parameters:
-- path: (required) The path of the directory to search in (relative to the current workspace directory /test/path). This directory will be recursively searched.
-- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
-- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
-
-Usage:
-
-Directory path here
-Your regex pattern here
-file pattern here (optional)
-
-
-Example: Searching for all .ts files in the current directory
-
-.
-.*
-*.ts
-
-
-Example: Searching for function definitions in JavaScript files
-
-src
-function\s+\w+
-*.js
-
-
-## list_files
-Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
-Parameters:
-- path: (required) The path of the directory to list contents for (relative to the current workspace directory /test/path)
-- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
-Usage:
-
-Directory path here
-true or false (optional)
-
-
-Example: Requesting to list all files in the current directory
-
-.
-false
-
-
-## write_to_file
-Description: Request to write content to a file. This tool is primarily used for **creating new files** or for scenarios where a **complete rewrite of an existing file is intentionally required**. If the file exists, it will be overwritten. If it doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
-
-**Important:** You should prefer using other editing tools over write_to_file when making changes to existing files, since write_to_file is slower and cannot handle large files. Use write_to_file primarily for new file creation.
-
-When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code.
-
-When creating a new project, organize all new files within a dedicated project directory unless the user specifies otherwise. Structure the project logically, adhering to best practices for the specific type of project being created.
-
-Parameters:
-- path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
-- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.
-
-Usage:
-
-File path here
-
-Your file content here
-
-
-
-Example: Writing a configuration file
-
-frontend-config.json
-
-{
- "apiEndpoint": "https://api.example.com",
- "theme": {
- "primaryColor": "#007bff",
- "secondaryColor": "#6c757d",
- "fontFamily": "Arial, sans-serif"
- },
- "features": {
- "darkMode": true,
- "notifications": true,
- "analytics": false
- },
- "version": "1.0.0"
-}
-
-
-
-## ask_followup_question
-Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively.
-
-Parameters:
-- question: (required) A clear, specific question addressing the information needed
-- follow_up: (required) A list of 2-4 suggested answers, each in its own tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
-
-Usage:
-
-Your question here
-
-First suggestion
-Action with mode switch
-
-
-
-Example:
-
-What is the path to the frontend-config.json file?
-
-./src/frontend-config.json
-./config/frontend-config.json
-./frontend-config.json
-
-
-
-## attempt_completion
-Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
-IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.
-Parameters:
-- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.
-Usage:
-
-
-Your final result description here
-
-
-
-Example: Requesting to attempt completion with a result
-
-
-I've updated the CSS
-
-
-
-## switch_mode
-Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch.
-Parameters:
-- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect")
-- reason: (optional) The reason for switching modes
-Usage:
-
-Mode slug here
-Reason for switching here
-
-
-Example: Requesting to switch to code mode
-
-code
-Need to make code changes
-
-
-## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-
-Example:
-
-code
-Implement a new feature for the application
-
-
-
-## update_todo_list
-
-**Description:**
-Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks.
-
-**Checklist Format:**
-- Use a single-level markdown checklist (no nesting or subtasks).
-- List todos in the intended execution order.
-- Status options:
- - [ ] Task description (pending)
- - [x] Task description (completed)
- - [-] Task description (in progress)
-
-**Status Rules:**
-- [ ] = pending (not started)
-- [x] = completed (fully finished, no unresolved issues)
-- [-] = in_progress (currently being worked on)
-
-**Core Principles:**
-- Before updating, always confirm which todos have been completed since the last update.
-- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress).
-- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately.
-- Do not remove any unfinished todos unless explicitly instructed.
-- Always retain all unfinished tasks, updating their status as needed.
-- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies).
-- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved.
-- Remove tasks only if they are no longer relevant or if the user requests deletion.
-
-**Usage Example:**
-
-
-[x] Analyze requirements
-[x] Design architecture
-[-] Implement core logic
-[ ] Write tests
-[ ] Update documentation
-
-
-
-*After completing "Implement core logic" and starting "Write tests":*
-
-
-[x] Analyze requirements
-[x] Design architecture
-[x] Implement core logic
-[-] Write tests
-[ ] Update documentation
-[ ] Add performance benchmarks
-
-
-
-**When to Use:**
-- The task is complicated or involves multiple steps or requires ongoing tracking.
-- You need to update the status of several todos at once.
-- New actionable items are discovered during task execution.
-- The user requests a todo list or provides multiple tasks.
-- The task is complex and benefits from clear, stepwise progress tracking.
-
-**When NOT to Use:**
-- There is only a single, trivial task.
-- The task can be completed in one or two simple steps.
-- The request is purely conversational or informational.
-
-**Task Management Guidelines:**
-- Mark task as completed immediately after all work of the current task is done.
-- Start the next task by marking it as in_progress.
-- Add new todos as soon as they are identified.
-- Use clear, descriptive task names.
-
-
-# Tool Use Guidelines
+ # Tool Use Guidelines
1. Assess what information you already have and what information you need to proceed with the task.
2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like `ls` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.
3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.
-4. Formulate your tool use using the XML format specified for each tool.
-5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
+4. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
- Information about whether the tool succeeded or failed, along with any reasons for failure.
- Linter errors that may have arisen due to the changes you made, which you'll need to address.
- New terminal output in reaction to the changes, which you may need to consider or act upon.
- Any other relevant feedback or information related to the tool use.
-6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
+5. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
It is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:
1. Confirm the success of each step before proceeding.
@@ -390,7 +53,7 @@ MODES
RULES
- The project base directory is: /test/path
-- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to .
+- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to execute_command.
- You cannot `cd` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path.
- Do not use the ~ character or $HOME to refer to the home directory.
- 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 '/test/path', and if so prepend with `cd`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run `npm install` in a project outside of '/test/path', 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)`.
diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap
index ee8a50e993..5305987e28 100644
--- a/src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap
+++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap
@@ -10,351 +10,19 @@ ALL responses MUST show ANY `language construct` OR filename reference as clicka
TOOL USE
-You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
+You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples. You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response.
-# Tool Use Formatting
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.
-
-# Tools
-
-## read_file
-Description: Request to read the contents of one or more files. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code. Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
-
-**IMPORTANT: You can read a maximum of 5 files in a single request.** If you need to read more files, use multiple sequential read_file requests.
-
-
-Parameters:
-- args: Contains one or more file elements, where each file contains:
- - path: (required) File path (relative to workspace directory /test/path)
-
-
-Usage:
-
-
-
- path/to/file
-
-
-
-
-
-Examples:
-
-1. Reading a single file:
-
-
-
- src/app.ts
-
-
-
-
-
-2. Reading multiple files (within the 5-file limit):
-
-
-
- src/app.ts
-
-
-
- src/utils.ts
-
-
-
-
-
-3. Reading an entire file:
-
-
-
- config.json
-
-
-
-
-IMPORTANT: You MUST use this Efficient Reading Strategy:
-- You MUST read all related files and implementations together in a single operation (up to 5 files at once)
-- You MUST obtain all necessary context before proceeding with changes
-
-- When you need to read more than 5 files, prioritize the most critical files first, then use subsequent read_file requests for additional files
-
-## fetch_instructions
-Description: Request to fetch instructions to perform a task
-Parameters:
-- task: (required) The task to get instructions for. This can take the following values:
- create_mcp_server
- create_mode
-
-Example: Requesting instructions to create an MCP Server
-
-
-create_mcp_server
-
-
-## search_files
-Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
-
-Craft your regex patterns carefully to balance specificity and flexibility. Use this tool to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include surrounding context, so analyze the surrounding code to better understand the matches. Leverage this 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.
-
-Parameters:
-- path: (required) The path of the directory to search in (relative to the current workspace directory /test/path). This directory will be recursively searched.
-- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
-- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
-
-Usage:
-
-Directory path here
-Your regex pattern here
-file pattern here (optional)
-
-
-Example: Searching for all .ts files in the current directory
-
-.
-.*
-*.ts
-
-
-Example: Searching for function definitions in JavaScript files
-
-src
-function\s+\w+
-*.js
-
-
-## list_files
-Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
-Parameters:
-- path: (required) The path of the directory to list contents for (relative to the current workspace directory /test/path)
-- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
-Usage:
-
-Directory path here
-true or false (optional)
-
-
-Example: Requesting to list all files in the current directory
-
-.
-false
-
-
-## write_to_file
-Description: Request to write content to a file. This tool is primarily used for **creating new files** or for scenarios where a **complete rewrite of an existing file is intentionally required**. If the file exists, it will be overwritten. If it doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
-
-**Important:** You should prefer using other editing tools over write_to_file when making changes to existing files, since write_to_file is slower and cannot handle large files. Use write_to_file primarily for new file creation.
-
-When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code.
-
-When creating a new project, organize all new files within a dedicated project directory unless the user specifies otherwise. Structure the project logically, adhering to best practices for the specific type of project being created.
-
-Parameters:
-- path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
-- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.
-
-Usage:
-
-File path here
-
-Your file content here
-
-
-
-Example: Writing a configuration file
-
-frontend-config.json
-
-{
- "apiEndpoint": "https://api.example.com",
- "theme": {
- "primaryColor": "#007bff",
- "secondaryColor": "#6c757d",
- "fontFamily": "Arial, sans-serif"
- },
- "features": {
- "darkMode": true,
- "notifications": true,
- "analytics": false
- },
- "version": "1.0.0"
-}
-
-
-
-## ask_followup_question
-Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively.
-
-Parameters:
-- question: (required) A clear, specific question addressing the information needed
-- follow_up: (required) A list of 2-4 suggested answers, each in its own tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
-
-Usage:
-
-Your question here
-
-First suggestion
-Action with mode switch
-
-
-
-Example:
-
-What is the path to the frontend-config.json file?
-
-./src/frontend-config.json
-./config/frontend-config.json
-./frontend-config.json
-
-
-
-## attempt_completion
-Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
-IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.
-Parameters:
-- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.
-Usage:
-
-
-Your final result description here
-
-
-
-Example: Requesting to attempt completion with a result
-
-
-I've updated the CSS
-
-
-
-## switch_mode
-Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch.
-Parameters:
-- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect")
-- reason: (optional) The reason for switching modes
-Usage:
-
-Mode slug here
-Reason for switching here
-
-
-Example: Requesting to switch to code mode
-
-code
-Need to make code changes
-
-
-## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-
-Example:
-
-code
-Implement a new feature for the application
-
-
-
-## update_todo_list
-
-**Description:**
-Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks.
-
-**Checklist Format:**
-- Use a single-level markdown checklist (no nesting or subtasks).
-- List todos in the intended execution order.
-- Status options:
- - [ ] Task description (pending)
- - [x] Task description (completed)
- - [-] Task description (in progress)
-
-**Status Rules:**
-- [ ] = pending (not started)
-- [x] = completed (fully finished, no unresolved issues)
-- [-] = in_progress (currently being worked on)
-
-**Core Principles:**
-- Before updating, always confirm which todos have been completed since the last update.
-- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress).
-- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately.
-- Do not remove any unfinished todos unless explicitly instructed.
-- Always retain all unfinished tasks, updating their status as needed.
-- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies).
-- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved.
-- Remove tasks only if they are no longer relevant or if the user requests deletion.
-
-**Usage Example:**
-
-
-[x] Analyze requirements
-[x] Design architecture
-[-] Implement core logic
-[ ] Write tests
-[ ] Update documentation
-
-
-
-*After completing "Implement core logic" and starting "Write tests":*
-
-
-[x] Analyze requirements
-[x] Design architecture
-[x] Implement core logic
-[-] Write tests
-[ ] Update documentation
-[ ] Add performance benchmarks
-
-
-
-**When to Use:**
-- The task is complicated or involves multiple steps or requires ongoing tracking.
-- You need to update the status of several todos at once.
-- New actionable items are discovered during task execution.
-- The user requests a todo list or provides multiple tasks.
-- The task is complex and benefits from clear, stepwise progress tracking.
-
-**When NOT to Use:**
-- There is only a single, trivial task.
-- The task can be completed in one or two simple steps.
-- The request is purely conversational or informational.
-
-**Task Management Guidelines:**
-- Mark task as completed immediately after all work of the current task is done.
-- Start the next task by marking it as in_progress.
-- Add new todos as soon as they are identified.
-- Use clear, descriptive task names.
-
-
-# Tool Use Guidelines
+ # Tool Use Guidelines
1. Assess what information you already have and what information you need to proceed with the task.
2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like `ls` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.
3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.
-4. Formulate your tool use using the XML format specified for each tool.
-5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
+4. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
- Information about whether the tool succeeded or failed, along with any reasons for failure.
- Linter errors that may have arisen due to the changes you made, which you'll need to address.
- New terminal output in reaction to the changes, which you may need to consider or act upon.
- Any other relevant feedback or information related to the tool use.
-6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
+5. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
It is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:
1. Confirm the success of each step before proceeding.
@@ -385,7 +53,7 @@ MODES
RULES
- The project base directory is: /test/path
-- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to .
+- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to execute_command.
- You cannot `cd` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path.
- Do not use the ~ character or $HOME to refer to the home directory.
- 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 '/test/path', and if so prepend with `cd`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run `npm install` in a project outside of '/test/path', 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)`.
@@ -434,7 +102,7 @@ You accomplish a given task iteratively, breaking it down into clear steps and w
USER'S CUSTOM INSTRUCTIONS
-The following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the TOOL USE guidelines.
+The following additional instructions are provided by the user, and should be followed to the best of your ability.
Language Preference:
You should always speak and think in the "en" language.
diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap
index 8edc23260e..5305987e28 100644
--- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap
+++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap
@@ -10,436 +10,19 @@ ALL responses MUST show ANY `language construct` OR filename reference as clicka
TOOL USE
-You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
+You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples. You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response.
-# Tool Use Formatting
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.
-
-# Tools
-
-## read_file
-Description: Request to read the contents of one or more files. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code. Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
-
-**IMPORTANT: You can read a maximum of 5 files in a single request.** If you need to read more files, use multiple sequential read_file requests.
-
-
-Parameters:
-- args: Contains one or more file elements, where each file contains:
- - path: (required) File path (relative to workspace directory /test/path)
-
-
-Usage:
-
-
-
- path/to/file
-
-
-
-
-
-Examples:
-
-1. Reading a single file:
-
-
-
- src/app.ts
-
-
-
-
-
-2. Reading multiple files (within the 5-file limit):
-
-
-
- src/app.ts
-
-
-
- src/utils.ts
-
-
-
-
-
-3. Reading an entire file:
-
-
-
- config.json
-
-
-
-
-IMPORTANT: You MUST use this Efficient Reading Strategy:
-- You MUST read all related files and implementations together in a single operation (up to 5 files at once)
-- You MUST obtain all necessary context before proceeding with changes
-
-- When you need to read more than 5 files, prioritize the most critical files first, then use subsequent read_file requests for additional files
-
-## fetch_instructions
-Description: Request to fetch instructions to perform a task
-Parameters:
-- task: (required) The task to get instructions for. This can take the following values:
- create_mcp_server
- create_mode
-
-Example: Requesting instructions to create an MCP Server
-
-
-create_mcp_server
-
-
-## search_files
-Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
-
-Craft your regex patterns carefully to balance specificity and flexibility. Use this tool to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include surrounding context, so analyze the surrounding code to better understand the matches. Leverage this 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.
-
-Parameters:
-- path: (required) The path of the directory to search in (relative to the current workspace directory /test/path). This directory will be recursively searched.
-- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
-- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
-
-Usage:
-
-Directory path here
-Your regex pattern here
-file pattern here (optional)
-
-
-Example: Searching for all .ts files in the current directory
-
-.
-.*
-*.ts
-
-
-Example: Searching for function definitions in JavaScript files
-
-src
-function\s+\w+
-*.js
-
-
-## list_files
-Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
-Parameters:
-- path: (required) The path of the directory to list contents for (relative to the current workspace directory /test/path)
-- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
-Usage:
-
-Directory path here
-true or false (optional)
-
-
-Example: Requesting to list all files in the current directory
-
-.
-false
-
-
-## write_to_file
-Description: Request to write content to a file. This tool is primarily used for **creating new files** or for scenarios where a **complete rewrite of an existing file is intentionally required**. If the file exists, it will be overwritten. If it doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
-
-**Important:** You should prefer using other editing tools over write_to_file when making changes to existing files, since write_to_file is slower and cannot handle large files. Use write_to_file primarily for new file creation.
-
-When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code.
-
-When creating a new project, organize all new files within a dedicated project directory unless the user specifies otherwise. Structure the project logically, adhering to best practices for the specific type of project being created.
-
-Parameters:
-- path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
-- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.
-
-Usage:
-
-File path here
-
-Your file content here
-
-
-
-Example: Writing a configuration file
-
-frontend-config.json
-
-{
- "apiEndpoint": "https://api.example.com",
- "theme": {
- "primaryColor": "#007bff",
- "secondaryColor": "#6c757d",
- "fontFamily": "Arial, sans-serif"
- },
- "features": {
- "darkMode": true,
- "notifications": true,
- "analytics": false
- },
- "version": "1.0.0"
-}
-
-
-
-## browser_action
-Description: Request to interact with a Puppeteer-controlled browser. Every action, except `close`, will be responded to with a screenshot of the browser's current state, along with any new console logs. You may only perform one browser action per message, and wait for the user's response including a screenshot and logs to determine the next action.
-
-This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. Use it at key stages of web development tasks - such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. Analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues.
-
-The user may ask generic non-development tasks (such as "what's the latest news" or "look up the weather"), in which case you might use this tool to complete the task if it makes sense to do so, rather than trying to create a website or using curl to answer the question. However, if an available MCP server tool or resource can be used instead, you should prefer to use it over browser_action.
-
-**Browser Session Lifecycle:**
-- Browser sessions **start** with `launch` and **end** with `close`
-- The session remains active across multiple messages and tool uses
-- You can use other tools while the browser session is active - it will stay open in the background
-
-Parameters:
-- action: (required) The action to perform. The available actions are:
- * launch: Launch a new Puppeteer-controlled browser instance at the specified URL. This **must always be the first action**.
- - Use with the `url` parameter to provide the URL.
- - Ensure the URL is valid and includes the appropriate protocol (e.g. http://localhost:3000/page, file:///path/to/file.html, etc.)
- * hover: Move the cursor to a specific x,y coordinate.
- - Use with the `coordinate` parameter to specify the location.
- - Always move to the center of an element (icon, button, link, etc.) based on coordinates derived from a screenshot.
- * click: Click at a specific x,y coordinate.
- - Use with the `coordinate` parameter to specify the location.
- - Always click in the center of an element (icon, button, link, etc.) based on coordinates derived from a screenshot.
- * type: Type a string of text on the keyboard. You might use this after clicking on a text field to input text.
- - Use with the `text` parameter to provide the string to type.
- * press: Press a single keyboard key or key combination (e.g., Enter, Tab, Escape, Cmd+K, Shift+Enter).
- - Use with the `text` parameter to provide the key name or combination.
- - For single keys: Enter, Tab, Escape, etc.
- - For key combinations: Cmd+K, Ctrl+C, Shift+Enter, Alt+F4, etc.
- - Supported modifiers: Cmd/Command/Meta, Ctrl/Control, Shift, Alt/Option
- - Example: Cmd+K or Shift+Enter
- * resize: Resize the viewport to a specific w,h size.
- - Use with the `size` parameter to specify the new size.
- * scroll_down: Scroll down the page by one page height.
- * scroll_up: Scroll up the page by one page height.
- * screenshot: Take a screenshot and save it to a file.
- - Use with the `path` parameter to specify the destination file path.
- - Supported formats: .png, .jpeg, .webp
- - Example: `screenshot` with `screenshots/result.png`
- * close: Close the Puppeteer-controlled browser instance. This **must always be the final browser action**.
- - Example: `close`
-- url: (optional) Use this for providing the URL for the `launch` action.
- * Example: https://example.com
-- coordinate: (optional) The X and Y coordinates for the `click` and `hover` actions.
- * **CRITICAL**: Screenshot dimensions are NOT the same as the browser viewport dimensions
- * Format: x,y@widthxheight
- * Measure x,y on the screenshot image you see in chat
- * The widthxheight MUST be the EXACT pixel size of that screenshot image (never the browser viewport)
- * Never use the browser viewport size for widthxheight - the viewport is only a reference and is often larger than the screenshot
- * Images are often downscaled before you see them, so the screenshot's dimensions will likely be smaller than the viewport
- * Example A: If the screenshot you see is 1094x1092 and you want to click (450,300) on that image, use: 450,300@1094x1092
- * Example B: If the browser viewport is 1280x800 but the screenshot is 1000x625 and you want to click (500,300) on the screenshot, use: 500,300@1000x625
-- size: (optional) The width and height for the `resize` action.
- * Example: 1280,720
-- text: (optional) Use this for providing the text for the `type` action.
- * Example: Hello, world!
-- path: (optional) File path for the `screenshot` action. Path is relative to the workspace.
- * Supported formats: .png, .jpeg, .webp
- * Example: screenshots/my-screenshot.png
-Usage:
-
-Action to perform (e.g., launch, click, type, press, scroll_down, scroll_up, close)
-URL to launch the browser at (optional)
-x,y@widthxheight coordinates (optional)
-Text to type (optional)
-
-
-Example: Requesting to launch a browser at https://example.com
-
-launch
-https://example.com
-
-
-Example: Requesting to click on the element at coordinates 450,300 on a 1024x768 image
-
-click
-450,300@1024x768
-
-
-Example: Taking a screenshot and saving it to a file
-
-screenshot
-screenshots/result.png
-
-
-## ask_followup_question
-Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively.
-
-Parameters:
-- question: (required) A clear, specific question addressing the information needed
-- follow_up: (required) A list of 2-4 suggested answers, each in its own tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
-
-Usage:
-
-Your question here
-
-First suggestion
-Action with mode switch
-
-
-
-Example:
-
-What is the path to the frontend-config.json file?
-
-./src/frontend-config.json
-./config/frontend-config.json
-./frontend-config.json
-
-
-
-## attempt_completion
-Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
-IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.
-Parameters:
-- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.
-Usage:
-
-
-Your final result description here
-
-
-
-Example: Requesting to attempt completion with a result
-
-
-I've updated the CSS
-
-
-
-## switch_mode
-Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch.
-Parameters:
-- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect")
-- reason: (optional) The reason for switching modes
-Usage:
-
-Mode slug here
-Reason for switching here
-
-
-Example: Requesting to switch to code mode
-
-code
-Need to make code changes
-
-
-## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-
-Example:
-
-code
-Implement a new feature for the application
-
-
-
-## update_todo_list
-
-**Description:**
-Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks.
-
-**Checklist Format:**
-- Use a single-level markdown checklist (no nesting or subtasks).
-- List todos in the intended execution order.
-- Status options:
- - [ ] Task description (pending)
- - [x] Task description (completed)
- - [-] Task description (in progress)
-
-**Status Rules:**
-- [ ] = pending (not started)
-- [x] = completed (fully finished, no unresolved issues)
-- [-] = in_progress (currently being worked on)
-
-**Core Principles:**
-- Before updating, always confirm which todos have been completed since the last update.
-- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress).
-- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately.
-- Do not remove any unfinished todos unless explicitly instructed.
-- Always retain all unfinished tasks, updating their status as needed.
-- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies).
-- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved.
-- Remove tasks only if they are no longer relevant or if the user requests deletion.
-
-**Usage Example:**
-
-
-[x] Analyze requirements
-[x] Design architecture
-[-] Implement core logic
-[ ] Write tests
-[ ] Update documentation
-
-
-
-*After completing "Implement core logic" and starting "Write tests":*
-
-
-[x] Analyze requirements
-[x] Design architecture
-[x] Implement core logic
-[-] Write tests
-[ ] Update documentation
-[ ] Add performance benchmarks
-
-
-
-**When to Use:**
-- The task is complicated or involves multiple steps or requires ongoing tracking.
-- You need to update the status of several todos at once.
-- New actionable items are discovered during task execution.
-- The user requests a todo list or provides multiple tasks.
-- The task is complex and benefits from clear, stepwise progress tracking.
-
-**When NOT to Use:**
-- There is only a single, trivial task.
-- The task can be completed in one or two simple steps.
-- The request is purely conversational or informational.
-
-**Task Management Guidelines:**
-- Mark task as completed immediately after all work of the current task is done.
-- Start the next task by marking it as in_progress.
-- Add new todos as soon as they are identified.
-- Use clear, descriptive task names.
-
-
-# Tool Use Guidelines
+ # Tool Use Guidelines
1. Assess what information you already have and what information you need to proceed with the task.
2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like `ls` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.
3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.
-4. Formulate your tool use using the XML format specified for each tool.
-5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
+4. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
- Information about whether the tool succeeded or failed, along with any reasons for failure.
- Linter errors that may have arisen due to the changes you made, which you'll need to address.
- New terminal output in reaction to the changes, which you may need to consider or act upon.
- Any other relevant feedback or information related to the tool use.
-6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
+5. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
It is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:
1. Confirm the success of each step before proceeding.
@@ -470,7 +53,7 @@ MODES
RULES
- The project base directory is: /test/path
-- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to .
+- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to execute_command.
- You cannot `cd` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path.
- Do not use the ~ character or $HOME to refer to the home directory.
- 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 '/test/path', and if so prepend with `cd`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run `npm install` in a project outside of '/test/path', 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)`.
@@ -519,7 +102,7 @@ You accomplish a given task iteratively, breaking it down into clear steps and w
USER'S CUSTOM INSTRUCTIONS
-The following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the TOOL USE guidelines.
+The following additional instructions are provided by the user, and should be followed to the best of your ability.
Language Preference:
You should always speak and think in the "en" language.
diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-false.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-false.snap
index ee8a50e993..5305987e28 100644
--- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-false.snap
+++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-false.snap
@@ -10,351 +10,19 @@ ALL responses MUST show ANY `language construct` OR filename reference as clicka
TOOL USE
-You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
+You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples. You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response.
-# Tool Use Formatting
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.
-
-# Tools
-
-## read_file
-Description: Request to read the contents of one or more files. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code. Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
-
-**IMPORTANT: You can read a maximum of 5 files in a single request.** If you need to read more files, use multiple sequential read_file requests.
-
-
-Parameters:
-- args: Contains one or more file elements, where each file contains:
- - path: (required) File path (relative to workspace directory /test/path)
-
-
-Usage:
-
-
-
- path/to/file
-
-
-
-
-
-Examples:
-
-1. Reading a single file:
-
-
-
- src/app.ts
-
-
-
-
-
-2. Reading multiple files (within the 5-file limit):
-
-
-
- src/app.ts
-
-
-
- src/utils.ts
-
-
-
-
-
-3. Reading an entire file:
-
-
-
- config.json
-
-
-
-
-IMPORTANT: You MUST use this Efficient Reading Strategy:
-- You MUST read all related files and implementations together in a single operation (up to 5 files at once)
-- You MUST obtain all necessary context before proceeding with changes
-
-- When you need to read more than 5 files, prioritize the most critical files first, then use subsequent read_file requests for additional files
-
-## fetch_instructions
-Description: Request to fetch instructions to perform a task
-Parameters:
-- task: (required) The task to get instructions for. This can take the following values:
- create_mcp_server
- create_mode
-
-Example: Requesting instructions to create an MCP Server
-
-
-create_mcp_server
-
-
-## search_files
-Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
-
-Craft your regex patterns carefully to balance specificity and flexibility. Use this tool to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include surrounding context, so analyze the surrounding code to better understand the matches. Leverage this 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.
-
-Parameters:
-- path: (required) The path of the directory to search in (relative to the current workspace directory /test/path). This directory will be recursively searched.
-- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
-- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
-
-Usage:
-
-Directory path here
-Your regex pattern here
-file pattern here (optional)
-
-
-Example: Searching for all .ts files in the current directory
-
-.
-.*
-*.ts
-
-
-Example: Searching for function definitions in JavaScript files
-
-src
-function\s+\w+
-*.js
-
-
-## list_files
-Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
-Parameters:
-- path: (required) The path of the directory to list contents for (relative to the current workspace directory /test/path)
-- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
-Usage:
-
-Directory path here
-true or false (optional)
-
-
-Example: Requesting to list all files in the current directory
-
-.
-false
-
-
-## write_to_file
-Description: Request to write content to a file. This tool is primarily used for **creating new files** or for scenarios where a **complete rewrite of an existing file is intentionally required**. If the file exists, it will be overwritten. If it doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
-
-**Important:** You should prefer using other editing tools over write_to_file when making changes to existing files, since write_to_file is slower and cannot handle large files. Use write_to_file primarily for new file creation.
-
-When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code.
-
-When creating a new project, organize all new files within a dedicated project directory unless the user specifies otherwise. Structure the project logically, adhering to best practices for the specific type of project being created.
-
-Parameters:
-- path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
-- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.
-
-Usage:
-
-File path here
-
-Your file content here
-
-
-
-Example: Writing a configuration file
-
-frontend-config.json
-
-{
- "apiEndpoint": "https://api.example.com",
- "theme": {
- "primaryColor": "#007bff",
- "secondaryColor": "#6c757d",
- "fontFamily": "Arial, sans-serif"
- },
- "features": {
- "darkMode": true,
- "notifications": true,
- "analytics": false
- },
- "version": "1.0.0"
-}
-
-
-
-## ask_followup_question
-Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively.
-
-Parameters:
-- question: (required) A clear, specific question addressing the information needed
-- follow_up: (required) A list of 2-4 suggested answers, each in its own tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
-
-Usage:
-
-Your question here
-
-First suggestion
-Action with mode switch
-
-
-
-Example:
-
-What is the path to the frontend-config.json file?
-
-./src/frontend-config.json
-./config/frontend-config.json
-./frontend-config.json
-
-
-
-## attempt_completion
-Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
-IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.
-Parameters:
-- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.
-Usage:
-
-
-Your final result description here
-
-
-
-Example: Requesting to attempt completion with a result
-
-
-I've updated the CSS
-
-
-
-## switch_mode
-Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch.
-Parameters:
-- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect")
-- reason: (optional) The reason for switching modes
-Usage:
-
-Mode slug here
-Reason for switching here
-
-
-Example: Requesting to switch to code mode
-
-code
-Need to make code changes
-
-
-## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-
-Example:
-
-code
-Implement a new feature for the application
-
-
-
-## update_todo_list
-
-**Description:**
-Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks.
-
-**Checklist Format:**
-- Use a single-level markdown checklist (no nesting or subtasks).
-- List todos in the intended execution order.
-- Status options:
- - [ ] Task description (pending)
- - [x] Task description (completed)
- - [-] Task description (in progress)
-
-**Status Rules:**
-- [ ] = pending (not started)
-- [x] = completed (fully finished, no unresolved issues)
-- [-] = in_progress (currently being worked on)
-
-**Core Principles:**
-- Before updating, always confirm which todos have been completed since the last update.
-- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress).
-- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately.
-- Do not remove any unfinished todos unless explicitly instructed.
-- Always retain all unfinished tasks, updating their status as needed.
-- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies).
-- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved.
-- Remove tasks only if they are no longer relevant or if the user requests deletion.
-
-**Usage Example:**
-
-
-[x] Analyze requirements
-[x] Design architecture
-[-] Implement core logic
-[ ] Write tests
-[ ] Update documentation
-
-
-
-*After completing "Implement core logic" and starting "Write tests":*
-
-
-[x] Analyze requirements
-[x] Design architecture
-[x] Implement core logic
-[-] Write tests
-[ ] Update documentation
-[ ] Add performance benchmarks
-
-
-
-**When to Use:**
-- The task is complicated or involves multiple steps or requires ongoing tracking.
-- You need to update the status of several todos at once.
-- New actionable items are discovered during task execution.
-- The user requests a todo list or provides multiple tasks.
-- The task is complex and benefits from clear, stepwise progress tracking.
-
-**When NOT to Use:**
-- There is only a single, trivial task.
-- The task can be completed in one or two simple steps.
-- The request is purely conversational or informational.
-
-**Task Management Guidelines:**
-- Mark task as completed immediately after all work of the current task is done.
-- Start the next task by marking it as in_progress.
-- Add new todos as soon as they are identified.
-- Use clear, descriptive task names.
-
-
-# Tool Use Guidelines
+ # Tool Use Guidelines
1. Assess what information you already have and what information you need to proceed with the task.
2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like `ls` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.
3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.
-4. Formulate your tool use using the XML format specified for each tool.
-5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
+4. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
- Information about whether the tool succeeded or failed, along with any reasons for failure.
- Linter errors that may have arisen due to the changes you made, which you'll need to address.
- New terminal output in reaction to the changes, which you may need to consider or act upon.
- Any other relevant feedback or information related to the tool use.
-6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
+5. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
It is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:
1. Confirm the success of each step before proceeding.
@@ -385,7 +53,7 @@ MODES
RULES
- The project base directory is: /test/path
-- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to .
+- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to execute_command.
- You cannot `cd` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path.
- Do not use the ~ character or $HOME to refer to the home directory.
- 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 '/test/path', and if so prepend with `cd`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run `npm install` in a project outside of '/test/path', 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)`.
@@ -434,7 +102,7 @@ You accomplish a given task iteratively, breaking it down into clear steps and w
USER'S CUSTOM INSTRUCTIONS
-The following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the TOOL USE guidelines.
+The following additional instructions are provided by the user, and should be followed to the best of your ability.
Language Preference:
You should always speak and think in the "en" language.
diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-true.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-true.snap
index 54df428abd..5305987e28 100644
--- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-true.snap
+++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-true.snap
@@ -10,439 +10,19 @@ ALL responses MUST show ANY `language construct` OR filename reference as clicka
TOOL USE
-You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
+You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples. You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response.
-# Tool Use Formatting
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.
-
-# Tools
-
-## read_file
-Description: Request to read the contents of one or more files. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code. Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
-
-**IMPORTANT: You can read a maximum of 5 files in a single request.** If you need to read more files, use multiple sequential read_file requests.
-
-
-Parameters:
-- args: Contains one or more file elements, where each file contains:
- - path: (required) File path (relative to workspace directory /test/path)
-
-
-Usage:
-
-
-
- path/to/file
-
-
-
-
-
-Examples:
-
-1. Reading a single file:
-
-
-
- src/app.ts
-
-
-
-
-
-2. Reading multiple files (within the 5-file limit):
-
-
-
- src/app.ts
-
-
-
- src/utils.ts
-
-
-
-
-
-3. Reading an entire file:
-
-
-
- config.json
-
-
-
-
-IMPORTANT: You MUST use this Efficient Reading Strategy:
-- You MUST read all related files and implementations together in a single operation (up to 5 files at once)
-- You MUST obtain all necessary context before proceeding with changes
-
-- When you need to read more than 5 files, prioritize the most critical files first, then use subsequent read_file requests for additional files
-
-## fetch_instructions
-Description: Request to fetch instructions to perform a task
-Parameters:
-- task: (required) The task to get instructions for. This can take the following values:
- create_mcp_server
- create_mode
-
-Example: Requesting instructions to create an MCP Server
-
-
-create_mcp_server
-
-
-## search_files
-Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
-
-Craft your regex patterns carefully to balance specificity and flexibility. Use this tool to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include surrounding context, so analyze the surrounding code to better understand the matches. Leverage this 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.
-
-Parameters:
-- path: (required) The path of the directory to search in (relative to the current workspace directory /test/path). This directory will be recursively searched.
-- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
-- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
-
-Usage:
-
-Directory path here
-Your regex pattern here
-file pattern here (optional)
-
-
-Example: Searching for all .ts files in the current directory
-
-.
-.*
-*.ts
-
-
-Example: Searching for function definitions in JavaScript files
-
-src
-function\s+\w+
-*.js
-
-
-## list_files
-Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
-Parameters:
-- path: (required) The path of the directory to list contents for (relative to the current workspace directory /test/path)
-- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
-Usage:
-
-Directory path here
-true or false (optional)
-
-
-Example: Requesting to list all files in the current directory
-
-.
-false
-
-
-## apply_diff
-Description: Request to apply PRECISE, TARGETED modifications to an existing file by searching for specific sections of content and replacing them. This tool is for SURGICAL EDITS ONLY - specific changes to existing code.
-You can perform multiple distinct search and replace operations within a single `apply_diff` call by providing multiple SEARCH/REPLACE blocks in the `diff` parameter. This is the preferred way to make several targeted changes efficiently.
-The SEARCH section must exactly match existing content including whitespace and indentation.
-If you're not confident in the exact content to search for, use the read_file tool first to get the exact content.
-When applying the diffs, be extra careful to remember to change any closing brackets or other syntax that may be affected by the diff farther down in the file.
-ALWAYS make as many changes in a single 'apply_diff' request as possible using multiple SEARCH/REPLACE blocks
-
-Parameters:
-- path: (required) The path of the file to modify (relative to the current workspace directory /test/path)
-- diff: (required) The search/replace block defining the changes.
-
-Diff format:
-```
-<<<<<<< SEARCH
-:start_line: (required) The line number of original content where the search block starts.
--------
-[exact content to find including whitespace]
-=======
-[new content to replace with]
->>>>>>> REPLACE
-
-```
-
-
-Example:
-
-Original file:
-```
-1 | def calculate_total(items):
-2 | total = 0
-3 | for item in items:
-4 | total += item
-5 | return total
-```
-
-Search/Replace content:
-```
-<<<<<<< SEARCH
-:start_line:1
--------
-def calculate_total(items):
- total = 0
- for item in items:
- total += item
- return total
-=======
-def calculate_total(items):
- """Calculate total with 10% markup"""
- return sum(item * 1.1 for item in items)
->>>>>>> REPLACE
-
-```
-
-Search/Replace content with multiple edits:
-```
-<<<<<<< SEARCH
-:start_line:1
--------
-def calculate_total(items):
- sum = 0
-=======
-def calculate_sum(items):
- sum = 0
->>>>>>> REPLACE
-
-<<<<<<< SEARCH
-:start_line:4
--------
- total += item
- return total
-=======
- sum += item
- return sum
->>>>>>> REPLACE
-```
-
-
-Usage:
-
-File path here
-
-Your search/replace content here
-You can use multi search/replace block in one diff block, but make sure to include the line numbers for each block.
-Only use a single line of '=======' between search and replacement content, because multiple '=======' will corrupt the file.
-
-
-
-## write_to_file
-Description: Request to write content to a file. This tool is primarily used for **creating new files** or for scenarios where a **complete rewrite of an existing file is intentionally required**. If the file exists, it will be overwritten. If it doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
-
-**Important:** You should prefer using other editing tools over write_to_file when making changes to existing files, since write_to_file is slower and cannot handle large files. Use write_to_file primarily for new file creation.
-
-When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code.
-
-When creating a new project, organize all new files within a dedicated project directory unless the user specifies otherwise. Structure the project logically, adhering to best practices for the specific type of project being created.
-
-Parameters:
-- path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
-- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.
-
-Usage:
-
-File path here
-
-Your file content here
-
-
-
-Example: Writing a configuration file
-
-frontend-config.json
-
-{
- "apiEndpoint": "https://api.example.com",
- "theme": {
- "primaryColor": "#007bff",
- "secondaryColor": "#6c757d",
- "fontFamily": "Arial, sans-serif"
- },
- "features": {
- "darkMode": true,
- "notifications": true,
- "analytics": false
- },
- "version": "1.0.0"
-}
-
-
-
-## ask_followup_question
-Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively.
-
-Parameters:
-- question: (required) A clear, specific question addressing the information needed
-- follow_up: (required) A list of 2-4 suggested answers, each in its own tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
-
-Usage:
-
-Your question here
-
-First suggestion
-Action with mode switch
-
-
-
-Example:
-
-What is the path to the frontend-config.json file?
-
-./src/frontend-config.json
-./config/frontend-config.json
-./frontend-config.json
-
-
-
-## attempt_completion
-Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
-IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.
-Parameters:
-- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.
-Usage:
-
-
-Your final result description here
-
-
-
-Example: Requesting to attempt completion with a result
-
-
-I've updated the CSS
-
-
-
-## switch_mode
-Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch.
-Parameters:
-- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect")
-- reason: (optional) The reason for switching modes
-Usage:
-
-Mode slug here
-Reason for switching here
-
-
-Example: Requesting to switch to code mode
-
-code
-Need to make code changes
-
-
-## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-
-Example:
-
-code
-Implement a new feature for the application
-
-
-
-## update_todo_list
-
-**Description:**
-Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks.
-
-**Checklist Format:**
-- Use a single-level markdown checklist (no nesting or subtasks).
-- List todos in the intended execution order.
-- Status options:
- - [ ] Task description (pending)
- - [x] Task description (completed)
- - [-] Task description (in progress)
-
-**Status Rules:**
-- [ ] = pending (not started)
-- [x] = completed (fully finished, no unresolved issues)
-- [-] = in_progress (currently being worked on)
-
-**Core Principles:**
-- Before updating, always confirm which todos have been completed since the last update.
-- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress).
-- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately.
-- Do not remove any unfinished todos unless explicitly instructed.
-- Always retain all unfinished tasks, updating their status as needed.
-- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies).
-- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved.
-- Remove tasks only if they are no longer relevant or if the user requests deletion.
-
-**Usage Example:**
-
-
-[x] Analyze requirements
-[x] Design architecture
-[-] Implement core logic
-[ ] Write tests
-[ ] Update documentation
-
-
-
-*After completing "Implement core logic" and starting "Write tests":*
-
-
-[x] Analyze requirements
-[x] Design architecture
-[x] Implement core logic
-[-] Write tests
-[ ] Update documentation
-[ ] Add performance benchmarks
-
-
-
-**When to Use:**
-- The task is complicated or involves multiple steps or requires ongoing tracking.
-- You need to update the status of several todos at once.
-- New actionable items are discovered during task execution.
-- The user requests a todo list or provides multiple tasks.
-- The task is complex and benefits from clear, stepwise progress tracking.
-
-**When NOT to Use:**
-- There is only a single, trivial task.
-- The task can be completed in one or two simple steps.
-- The request is purely conversational or informational.
-
-**Task Management Guidelines:**
-- Mark task as completed immediately after all work of the current task is done.
-- Start the next task by marking it as in_progress.
-- Add new todos as soon as they are identified.
-- Use clear, descriptive task names.
-
-
-# Tool Use Guidelines
+ # Tool Use Guidelines
1. Assess what information you already have and what information you need to proceed with the task.
2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like `ls` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.
3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.
-4. Formulate your tool use using the XML format specified for each tool.
-5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
+4. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
- Information about whether the tool succeeded or failed, along with any reasons for failure.
- Linter errors that may have arisen due to the changes you made, which you'll need to address.
- New terminal output in reaction to the changes, which you may need to consider or act upon.
- Any other relevant feedback or information related to the tool use.
-6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
+5. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
It is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:
1. Confirm the success of each step before proceeding.
@@ -473,7 +53,7 @@ MODES
RULES
- The project base directory is: /test/path
-- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to .
+- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to execute_command.
- You cannot `cd` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path.
- Do not use the ~ character or $HOME to refer to the home directory.
- 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 '/test/path', and if so prepend with `cd`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run `npm install` in a project outside of '/test/path', 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)`.
@@ -522,7 +102,7 @@ You accomplish a given task iteratively, breaking it down into clear steps and w
USER'S CUSTOM INSTRUCTIONS
-The following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the TOOL USE guidelines.
+The following additional instructions are provided by the user, and should be followed to the best of your ability.
Language Preference:
You should always speak and think in the "en" language.
diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-undefined.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-undefined.snap
index ee8a50e993..5305987e28 100644
--- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-undefined.snap
+++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-undefined.snap
@@ -10,351 +10,19 @@ ALL responses MUST show ANY `language construct` OR filename reference as clicka
TOOL USE
-You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
+You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples. You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response.
-# Tool Use Formatting
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.
-
-# Tools
-
-## read_file
-Description: Request to read the contents of one or more files. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code. Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
-
-**IMPORTANT: You can read a maximum of 5 files in a single request.** If you need to read more files, use multiple sequential read_file requests.
-
-
-Parameters:
-- args: Contains one or more file elements, where each file contains:
- - path: (required) File path (relative to workspace directory /test/path)
-
-
-Usage:
-
-
-
- path/to/file
-
-
-
-
-
-Examples:
-
-1. Reading a single file:
-
-
-
- src/app.ts
-
-
-
-
-
-2. Reading multiple files (within the 5-file limit):
-
-
-
- src/app.ts
-
-
-
- src/utils.ts
-
-
-
-
-
-3. Reading an entire file:
-
-
-
- config.json
-
-
-
-
-IMPORTANT: You MUST use this Efficient Reading Strategy:
-- You MUST read all related files and implementations together in a single operation (up to 5 files at once)
-- You MUST obtain all necessary context before proceeding with changes
-
-- When you need to read more than 5 files, prioritize the most critical files first, then use subsequent read_file requests for additional files
-
-## fetch_instructions
-Description: Request to fetch instructions to perform a task
-Parameters:
-- task: (required) The task to get instructions for. This can take the following values:
- create_mcp_server
- create_mode
-
-Example: Requesting instructions to create an MCP Server
-
-
-create_mcp_server
-
-
-## search_files
-Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
-
-Craft your regex patterns carefully to balance specificity and flexibility. Use this tool to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include surrounding context, so analyze the surrounding code to better understand the matches. Leverage this 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.
-
-Parameters:
-- path: (required) The path of the directory to search in (relative to the current workspace directory /test/path). This directory will be recursively searched.
-- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
-- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
-
-Usage:
-
-Directory path here
-Your regex pattern here
-file pattern here (optional)
-
-
-Example: Searching for all .ts files in the current directory
-
-.
-.*
-*.ts
-
-
-Example: Searching for function definitions in JavaScript files
-
-src
-function\s+\w+
-*.js
-
-
-## list_files
-Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
-Parameters:
-- path: (required) The path of the directory to list contents for (relative to the current workspace directory /test/path)
-- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
-Usage:
-
-Directory path here
-true or false (optional)
-
-
-Example: Requesting to list all files in the current directory
-
-.
-false
-
-
-## write_to_file
-Description: Request to write content to a file. This tool is primarily used for **creating new files** or for scenarios where a **complete rewrite of an existing file is intentionally required**. If the file exists, it will be overwritten. If it doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
-
-**Important:** You should prefer using other editing tools over write_to_file when making changes to existing files, since write_to_file is slower and cannot handle large files. Use write_to_file primarily for new file creation.
-
-When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code.
-
-When creating a new project, organize all new files within a dedicated project directory unless the user specifies otherwise. Structure the project logically, adhering to best practices for the specific type of project being created.
-
-Parameters:
-- path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
-- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.
-
-Usage:
-
-File path here
-
-Your file content here
-
-
-
-Example: Writing a configuration file
-
-frontend-config.json
-
-{
- "apiEndpoint": "https://api.example.com",
- "theme": {
- "primaryColor": "#007bff",
- "secondaryColor": "#6c757d",
- "fontFamily": "Arial, sans-serif"
- },
- "features": {
- "darkMode": true,
- "notifications": true,
- "analytics": false
- },
- "version": "1.0.0"
-}
-
-
-
-## ask_followup_question
-Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively.
-
-Parameters:
-- question: (required) A clear, specific question addressing the information needed
-- follow_up: (required) A list of 2-4 suggested answers, each in its own tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
-
-Usage:
-
-Your question here
-
-First suggestion
-Action with mode switch
-
-
-
-Example:
-
-What is the path to the frontend-config.json file?
-
-./src/frontend-config.json
-./config/frontend-config.json
-./frontend-config.json
-
-
-
-## attempt_completion
-Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
-IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.
-Parameters:
-- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.
-Usage:
-
-
-Your final result description here
-
-
-
-Example: Requesting to attempt completion with a result
-
-
-I've updated the CSS
-
-
-
-## switch_mode
-Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch.
-Parameters:
-- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect")
-- reason: (optional) The reason for switching modes
-Usage:
-
-Mode slug here
-Reason for switching here
-
-
-Example: Requesting to switch to code mode
-
-code
-Need to make code changes
-
-
-## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-
-Example:
-
-code
-Implement a new feature for the application
-
-
-
-## update_todo_list
-
-**Description:**
-Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks.
-
-**Checklist Format:**
-- Use a single-level markdown checklist (no nesting or subtasks).
-- List todos in the intended execution order.
-- Status options:
- - [ ] Task description (pending)
- - [x] Task description (completed)
- - [-] Task description (in progress)
-
-**Status Rules:**
-- [ ] = pending (not started)
-- [x] = completed (fully finished, no unresolved issues)
-- [-] = in_progress (currently being worked on)
-
-**Core Principles:**
-- Before updating, always confirm which todos have been completed since the last update.
-- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress).
-- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately.
-- Do not remove any unfinished todos unless explicitly instructed.
-- Always retain all unfinished tasks, updating their status as needed.
-- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies).
-- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved.
-- Remove tasks only if they are no longer relevant or if the user requests deletion.
-
-**Usage Example:**
-
-
-[x] Analyze requirements
-[x] Design architecture
-[-] Implement core logic
-[ ] Write tests
-[ ] Update documentation
-
-
-
-*After completing "Implement core logic" and starting "Write tests":*
-
-
-[x] Analyze requirements
-[x] Design architecture
-[x] Implement core logic
-[-] Write tests
-[ ] Update documentation
-[ ] Add performance benchmarks
-
-
-
-**When to Use:**
-- The task is complicated or involves multiple steps or requires ongoing tracking.
-- You need to update the status of several todos at once.
-- New actionable items are discovered during task execution.
-- The user requests a todo list or provides multiple tasks.
-- The task is complex and benefits from clear, stepwise progress tracking.
-
-**When NOT to Use:**
-- There is only a single, trivial task.
-- The task can be completed in one or two simple steps.
-- The request is purely conversational or informational.
-
-**Task Management Guidelines:**
-- Mark task as completed immediately after all work of the current task is done.
-- Start the next task by marking it as in_progress.
-- Add new todos as soon as they are identified.
-- Use clear, descriptive task names.
-
-
-# Tool Use Guidelines
+ # Tool Use Guidelines
1. Assess what information you already have and what information you need to proceed with the task.
2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like `ls` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.
3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.
-4. Formulate your tool use using the XML format specified for each tool.
-5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
+4. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
- Information about whether the tool succeeded or failed, along with any reasons for failure.
- Linter errors that may have arisen due to the changes you made, which you'll need to address.
- New terminal output in reaction to the changes, which you may need to consider or act upon.
- Any other relevant feedback or information related to the tool use.
-6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
+5. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
It is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:
1. Confirm the success of each step before proceeding.
@@ -385,7 +53,7 @@ MODES
RULES
- The project base directory is: /test/path
-- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to .
+- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to execute_command.
- You cannot `cd` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path.
- Do not use the ~ character or $HOME to refer to the home directory.
- 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 '/test/path', and if so prepend with `cd`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run `npm install` in a project outside of '/test/path', 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)`.
@@ -434,7 +102,7 @@ You accomplish a given task iteratively, breaking it down into clear steps and w
USER'S CUSTOM INSTRUCTIONS
-The following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the TOOL USE guidelines.
+The following additional instructions are provided by the user, and should be followed to the best of your ability.
Language Preference:
You should always speak and think in the "en" language.
diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-different-viewport-size.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-different-viewport-size.snap
index ee8a50e993..5305987e28 100644
--- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-different-viewport-size.snap
+++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-different-viewport-size.snap
@@ -10,351 +10,19 @@ ALL responses MUST show ANY `language construct` OR filename reference as clicka
TOOL USE
-You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
+You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples. You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response.
-# Tool Use Formatting
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.
-
-# Tools
-
-## read_file
-Description: Request to read the contents of one or more files. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code. Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
-
-**IMPORTANT: You can read a maximum of 5 files in a single request.** If you need to read more files, use multiple sequential read_file requests.
-
-
-Parameters:
-- args: Contains one or more file elements, where each file contains:
- - path: (required) File path (relative to workspace directory /test/path)
-
-
-Usage:
-
-
-
- path/to/file
-
-
-
-
-
-Examples:
-
-1. Reading a single file:
-
-
-
- src/app.ts
-
-
-
-
-
-2. Reading multiple files (within the 5-file limit):
-
-
-
- src/app.ts
-
-
-
- src/utils.ts
-
-
-
-
-
-3. Reading an entire file:
-
-
-
- config.json
-
-
-
-
-IMPORTANT: You MUST use this Efficient Reading Strategy:
-- You MUST read all related files and implementations together in a single operation (up to 5 files at once)
-- You MUST obtain all necessary context before proceeding with changes
-
-- When you need to read more than 5 files, prioritize the most critical files first, then use subsequent read_file requests for additional files
-
-## fetch_instructions
-Description: Request to fetch instructions to perform a task
-Parameters:
-- task: (required) The task to get instructions for. This can take the following values:
- create_mcp_server
- create_mode
-
-Example: Requesting instructions to create an MCP Server
-
-
-create_mcp_server
-
-
-## search_files
-Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
-
-Craft your regex patterns carefully to balance specificity and flexibility. Use this tool to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include surrounding context, so analyze the surrounding code to better understand the matches. Leverage this 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.
-
-Parameters:
-- path: (required) The path of the directory to search in (relative to the current workspace directory /test/path). This directory will be recursively searched.
-- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
-- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
-
-Usage:
-
-Directory path here
-Your regex pattern here
-file pattern here (optional)
-
-
-Example: Searching for all .ts files in the current directory
-
-.
-.*
-*.ts
-
-
-Example: Searching for function definitions in JavaScript files
-
-src
-function\s+\w+
-*.js
-
-
-## list_files
-Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
-Parameters:
-- path: (required) The path of the directory to list contents for (relative to the current workspace directory /test/path)
-- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
-Usage:
-
-Directory path here
-true or false (optional)
-
-
-Example: Requesting to list all files in the current directory
-
-.
-false
-
-
-## write_to_file
-Description: Request to write content to a file. This tool is primarily used for **creating new files** or for scenarios where a **complete rewrite of an existing file is intentionally required**. If the file exists, it will be overwritten. If it doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
-
-**Important:** You should prefer using other editing tools over write_to_file when making changes to existing files, since write_to_file is slower and cannot handle large files. Use write_to_file primarily for new file creation.
-
-When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code.
-
-When creating a new project, organize all new files within a dedicated project directory unless the user specifies otherwise. Structure the project logically, adhering to best practices for the specific type of project being created.
-
-Parameters:
-- path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
-- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.
-
-Usage:
-
-File path here
-
-Your file content here
-
-
-
-Example: Writing a configuration file
-
-frontend-config.json
-
-{
- "apiEndpoint": "https://api.example.com",
- "theme": {
- "primaryColor": "#007bff",
- "secondaryColor": "#6c757d",
- "fontFamily": "Arial, sans-serif"
- },
- "features": {
- "darkMode": true,
- "notifications": true,
- "analytics": false
- },
- "version": "1.0.0"
-}
-
-
-
-## ask_followup_question
-Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively.
-
-Parameters:
-- question: (required) A clear, specific question addressing the information needed
-- follow_up: (required) A list of 2-4 suggested answers, each in its own tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
-
-Usage:
-
-Your question here
-
-First suggestion
-Action with mode switch
-
-
-
-Example:
-
-What is the path to the frontend-config.json file?
-
-./src/frontend-config.json
-./config/frontend-config.json
-./frontend-config.json
-
-
-
-## attempt_completion
-Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
-IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.
-Parameters:
-- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.
-Usage:
-
-
-Your final result description here
-
-
-
-Example: Requesting to attempt completion with a result
-
-
-I've updated the CSS
-
-
-
-## switch_mode
-Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch.
-Parameters:
-- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect")
-- reason: (optional) The reason for switching modes
-Usage:
-
-Mode slug here
-Reason for switching here
-
-
-Example: Requesting to switch to code mode
-
-code
-Need to make code changes
-
-
-## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-
-Example:
-
-code
-Implement a new feature for the application
-
-
-
-## update_todo_list
-
-**Description:**
-Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks.
-
-**Checklist Format:**
-- Use a single-level markdown checklist (no nesting or subtasks).
-- List todos in the intended execution order.
-- Status options:
- - [ ] Task description (pending)
- - [x] Task description (completed)
- - [-] Task description (in progress)
-
-**Status Rules:**
-- [ ] = pending (not started)
-- [x] = completed (fully finished, no unresolved issues)
-- [-] = in_progress (currently being worked on)
-
-**Core Principles:**
-- Before updating, always confirm which todos have been completed since the last update.
-- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress).
-- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately.
-- Do not remove any unfinished todos unless explicitly instructed.
-- Always retain all unfinished tasks, updating their status as needed.
-- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies).
-- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved.
-- Remove tasks only if they are no longer relevant or if the user requests deletion.
-
-**Usage Example:**
-
-
-[x] Analyze requirements
-[x] Design architecture
-[-] Implement core logic
-[ ] Write tests
-[ ] Update documentation
-
-
-
-*After completing "Implement core logic" and starting "Write tests":*
-
-
-[x] Analyze requirements
-[x] Design architecture
-[x] Implement core logic
-[-] Write tests
-[ ] Update documentation
-[ ] Add performance benchmarks
-
-
-
-**When to Use:**
-- The task is complicated or involves multiple steps or requires ongoing tracking.
-- You need to update the status of several todos at once.
-- New actionable items are discovered during task execution.
-- The user requests a todo list or provides multiple tasks.
-- The task is complex and benefits from clear, stepwise progress tracking.
-
-**When NOT to Use:**
-- There is only a single, trivial task.
-- The task can be completed in one or two simple steps.
-- The request is purely conversational or informational.
-
-**Task Management Guidelines:**
-- Mark task as completed immediately after all work of the current task is done.
-- Start the next task by marking it as in_progress.
-- Add new todos as soon as they are identified.
-- Use clear, descriptive task names.
-
-
-# Tool Use Guidelines
+ # Tool Use Guidelines
1. Assess what information you already have and what information you need to proceed with the task.
2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like `ls` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.
3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.
-4. Formulate your tool use using the XML format specified for each tool.
-5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
+4. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
- Information about whether the tool succeeded or failed, along with any reasons for failure.
- Linter errors that may have arisen due to the changes you made, which you'll need to address.
- New terminal output in reaction to the changes, which you may need to consider or act upon.
- Any other relevant feedback or information related to the tool use.
-6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
+5. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
It is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:
1. Confirm the success of each step before proceeding.
@@ -385,7 +53,7 @@ MODES
RULES
- The project base directory is: /test/path
-- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to .
+- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to execute_command.
- You cannot `cd` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path.
- Do not use the ~ character or $HOME to refer to the home directory.
- 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 '/test/path', and if so prepend with `cd`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run `npm install` in a project outside of '/test/path', 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)`.
@@ -434,7 +102,7 @@ You accomplish a given task iteratively, breaking it down into clear steps and w
USER'S CUSTOM INSTRUCTIONS
-The following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the TOOL USE guidelines.
+The following additional instructions are provided by the user, and should be followed to the best of your ability.
Language Preference:
You should always speak and think in the "en" language.
diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap
index acc36d1ffd..baa8d519d8 100644
--- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap
+++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap
@@ -10,400 +10,19 @@ ALL responses MUST show ANY `language construct` OR filename reference as clicka
TOOL USE
-You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
+You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples. You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response.
-# Tool Use Formatting
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.
-
-# Tools
-
-## read_file
-Description: Request to read the contents of one or more files. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code. Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
-
-**IMPORTANT: You can read a maximum of 5 files in a single request.** If you need to read more files, use multiple sequential read_file requests.
-
-
-Parameters:
-- args: Contains one or more file elements, where each file contains:
- - path: (required) File path (relative to workspace directory /test/path)
-
-
-Usage:
-
-
-
- path/to/file
-
-
-
-
-
-Examples:
-
-1. Reading a single file:
-
-
-
- src/app.ts
-
-
-
-
-
-2. Reading multiple files (within the 5-file limit):
-
-
-
- src/app.ts
-
-
-
- src/utils.ts
-
-
-
-
-
-3. Reading an entire file:
-
-
-
- config.json
-
-
-
-
-IMPORTANT: You MUST use this Efficient Reading Strategy:
-- You MUST read all related files and implementations together in a single operation (up to 5 files at once)
-- You MUST obtain all necessary context before proceeding with changes
-
-- When you need to read more than 5 files, prioritize the most critical files first, then use subsequent read_file requests for additional files
-
-## fetch_instructions
-Description: Request to fetch instructions to perform a task
-Parameters:
-- task: (required) The task to get instructions for. This can take the following values:
- create_mcp_server
- create_mode
-
-Example: Requesting instructions to create an MCP Server
-
-
-create_mcp_server
-
-
-## search_files
-Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
-
-Craft your regex patterns carefully to balance specificity and flexibility. Use this tool to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include surrounding context, so analyze the surrounding code to better understand the matches. Leverage this 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.
-
-Parameters:
-- path: (required) The path of the directory to search in (relative to the current workspace directory /test/path). This directory will be recursively searched.
-- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
-- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
-
-Usage:
-
-Directory path here
-Your regex pattern here
-file pattern here (optional)
-
-
-Example: Searching for all .ts files in the current directory
-
-.
-.*
-*.ts
-
-
-Example: Searching for function definitions in JavaScript files
-
-src
-function\s+\w+
-*.js
-
-
-## list_files
-Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
-Parameters:
-- path: (required) The path of the directory to list contents for (relative to the current workspace directory /test/path)
-- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
-Usage:
-
-Directory path here
-true or false (optional)
-
-
-Example: Requesting to list all files in the current directory
-
-.
-false
-
-
-## write_to_file
-Description: Request to write content to a file. This tool is primarily used for **creating new files** or for scenarios where a **complete rewrite of an existing file is intentionally required**. If the file exists, it will be overwritten. If it doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
-
-**Important:** You should prefer using other editing tools over write_to_file when making changes to existing files, since write_to_file is slower and cannot handle large files. Use write_to_file primarily for new file creation.
-
-When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code.
-
-When creating a new project, organize all new files within a dedicated project directory unless the user specifies otherwise. Structure the project logically, adhering to best practices for the specific type of project being created.
-
-Parameters:
-- path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
-- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.
-
-Usage:
-
-File path here
-
-Your file content here
-
-
-
-Example: Writing a configuration file
-
-frontend-config.json
-
-{
- "apiEndpoint": "https://api.example.com",
- "theme": {
- "primaryColor": "#007bff",
- "secondaryColor": "#6c757d",
- "fontFamily": "Arial, sans-serif"
- },
- "features": {
- "darkMode": true,
- "notifications": true,
- "analytics": false
- },
- "version": "1.0.0"
-}
-
-
-
-## use_mcp_tool
-Description: Request to use a tool provided by a connected MCP server. Each MCP server can provide multiple tools with different capabilities. Tools have defined input schemas that specify required and optional parameters.
-Parameters:
-- server_name: (required) The name of the MCP server providing the tool
-- tool_name: (required) The name of the tool to execute
-- arguments: (required) A JSON object containing the tool's input parameters, following the tool's input schema
-Usage:
-
-server name here
-tool name here
-
-{
- "param1": "value1",
- "param2": "value2"
-}
-
-
-
-Example: Requesting to use an MCP tool
-
-
-weather-server
-get_forecast
-
-{
- "city": "San Francisco",
- "days": 5
-}
-
-
-
-## access_mcp_resource
-Description: Request to access a resource provided by a connected MCP server. Resources represent data sources that can be used as context, such as files, API responses, or system information.
-Parameters:
-- server_name: (required) The name of the MCP server providing the resource
-- uri: (required) The URI identifying the specific resource to access
-Usage:
-
-server name here
-resource URI here
-
-
-Example: Requesting to access an MCP resource
-
-
-weather-server
-weather://san-francisco/current
-
-
-## ask_followup_question
-Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively.
-
-Parameters:
-- question: (required) A clear, specific question addressing the information needed
-- follow_up: (required) A list of 2-4 suggested answers, each in its own tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
-
-Usage:
-
-Your question here
-
-First suggestion
-Action with mode switch
-
-
-
-Example:
-
-What is the path to the frontend-config.json file?
-
-./src/frontend-config.json
-./config/frontend-config.json
-./frontend-config.json
-
-
-
-## attempt_completion
-Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
-IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.
-Parameters:
-- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.
-Usage:
-
-
-Your final result description here
-
-
-
-Example: Requesting to attempt completion with a result
-
-
-I've updated the CSS
-
-
-
-## switch_mode
-Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch.
-Parameters:
-- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect")
-- reason: (optional) The reason for switching modes
-Usage:
-
-Mode slug here
-Reason for switching here
-
-
-Example: Requesting to switch to code mode
-
-code
-Need to make code changes
-
-
-## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-
-Example:
-
-code
-Implement a new feature for the application
-
-
-
-## update_todo_list
-
-**Description:**
-Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks.
-
-**Checklist Format:**
-- Use a single-level markdown checklist (no nesting or subtasks).
-- List todos in the intended execution order.
-- Status options:
- - [ ] Task description (pending)
- - [x] Task description (completed)
- - [-] Task description (in progress)
-
-**Status Rules:**
-- [ ] = pending (not started)
-- [x] = completed (fully finished, no unresolved issues)
-- [-] = in_progress (currently being worked on)
-
-**Core Principles:**
-- Before updating, always confirm which todos have been completed since the last update.
-- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress).
-- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately.
-- Do not remove any unfinished todos unless explicitly instructed.
-- Always retain all unfinished tasks, updating their status as needed.
-- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies).
-- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved.
-- Remove tasks only if they are no longer relevant or if the user requests deletion.
-
-**Usage Example:**
-
-
-[x] Analyze requirements
-[x] Design architecture
-[-] Implement core logic
-[ ] Write tests
-[ ] Update documentation
-
-
-
-*After completing "Implement core logic" and starting "Write tests":*
-
-
-[x] Analyze requirements
-[x] Design architecture
-[x] Implement core logic
-[-] Write tests
-[ ] Update documentation
-[ ] Add performance benchmarks
-
-
-
-**When to Use:**
-- The task is complicated or involves multiple steps or requires ongoing tracking.
-- You need to update the status of several todos at once.
-- New actionable items are discovered during task execution.
-- The user requests a todo list or provides multiple tasks.
-- The task is complex and benefits from clear, stepwise progress tracking.
-
-**When NOT to Use:**
-- There is only a single, trivial task.
-- The task can be completed in one or two simple steps.
-- The request is purely conversational or informational.
-
-**Task Management Guidelines:**
-- Mark task as completed immediately after all work of the current task is done.
-- Start the next task by marking it as in_progress.
-- Add new todos as soon as they are identified.
-- Use clear, descriptive task names.
-
-
-# Tool Use Guidelines
+ # Tool Use Guidelines
1. Assess what information you already have and what information you need to proceed with the task.
2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like `ls` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.
3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.
-4. Formulate your tool use using the XML format specified for each tool.
-5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
+4. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
- Information about whether the tool succeeded or failed, along with any reasons for failure.
- Linter errors that may have arisen due to the changes you made, which you'll need to address.
- New terminal output in reaction to the changes, which you may need to consider or act upon.
- Any other relevant feedback or information related to the tool use.
-6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
+5. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
It is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:
1. Confirm the success of each step before proceeding.
@@ -453,7 +72,7 @@ MODES
RULES
- The project base directory is: /test/path
-- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to .
+- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to execute_command.
- You cannot `cd` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path.
- Do not use the ~ character or $HOME to refer to the home directory.
- 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 '/test/path', and if so prepend with `cd`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run `npm install` in a project outside of '/test/path', 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)`.
@@ -502,7 +121,7 @@ You accomplish a given task iteratively, breaking it down into clear steps and w
USER'S CUSTOM INSTRUCTIONS
-The following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the TOOL USE guidelines.
+The following additional instructions are provided by the user, and should be followed to the best of your ability.
Language Preference:
You should always speak and think in the "en" language.
diff --git a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap
index ee8a50e993..5305987e28 100644
--- a/src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap
+++ b/src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap
@@ -10,351 +10,19 @@ ALL responses MUST show ANY `language construct` OR filename reference as clicka
TOOL USE
-You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
+You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples. You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response.
-# Tool Use Formatting
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.
-
-# Tools
-
-## read_file
-Description: Request to read the contents of one or more files. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code. Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
-
-**IMPORTANT: You can read a maximum of 5 files in a single request.** If you need to read more files, use multiple sequential read_file requests.
-
-
-Parameters:
-- args: Contains one or more file elements, where each file contains:
- - path: (required) File path (relative to workspace directory /test/path)
-
-
-Usage:
-
-
-
- path/to/file
-
-
-
-
-
-Examples:
-
-1. Reading a single file:
-
-
-
- src/app.ts
-
-
-
-
-
-2. Reading multiple files (within the 5-file limit):
-
-
-
- src/app.ts
-
-
-
- src/utils.ts
-
-
-
-
-
-3. Reading an entire file:
-
-
-
- config.json
-
-
-
-
-IMPORTANT: You MUST use this Efficient Reading Strategy:
-- You MUST read all related files and implementations together in a single operation (up to 5 files at once)
-- You MUST obtain all necessary context before proceeding with changes
-
-- When you need to read more than 5 files, prioritize the most critical files first, then use subsequent read_file requests for additional files
-
-## fetch_instructions
-Description: Request to fetch instructions to perform a task
-Parameters:
-- task: (required) The task to get instructions for. This can take the following values:
- create_mcp_server
- create_mode
-
-Example: Requesting instructions to create an MCP Server
-
-
-create_mcp_server
-
-
-## search_files
-Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
-
-Craft your regex patterns carefully to balance specificity and flexibility. Use this tool to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include surrounding context, so analyze the surrounding code to better understand the matches. Leverage this 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.
-
-Parameters:
-- path: (required) The path of the directory to search in (relative to the current workspace directory /test/path). This directory will be recursively searched.
-- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
-- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
-
-Usage:
-
-Directory path here
-Your regex pattern here
-file pattern here (optional)
-
-
-Example: Searching for all .ts files in the current directory
-
-.
-.*
-*.ts
-
-
-Example: Searching for function definitions in JavaScript files
-
-src
-function\s+\w+
-*.js
-
-
-## list_files
-Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
-Parameters:
-- path: (required) The path of the directory to list contents for (relative to the current workspace directory /test/path)
-- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
-Usage:
-
-Directory path here
-true or false (optional)
-
-
-Example: Requesting to list all files in the current directory
-
-.
-false
-
-
-## write_to_file
-Description: Request to write content to a file. This tool is primarily used for **creating new files** or for scenarios where a **complete rewrite of an existing file is intentionally required**. If the file exists, it will be overwritten. If it doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
-
-**Important:** You should prefer using other editing tools over write_to_file when making changes to existing files, since write_to_file is slower and cannot handle large files. Use write_to_file primarily for new file creation.
-
-When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code.
-
-When creating a new project, organize all new files within a dedicated project directory unless the user specifies otherwise. Structure the project logically, adhering to best practices for the specific type of project being created.
-
-Parameters:
-- path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
-- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.
-
-Usage:
-
-File path here
-
-Your file content here
-
-
-
-Example: Writing a configuration file
-
-frontend-config.json
-
-{
- "apiEndpoint": "https://api.example.com",
- "theme": {
- "primaryColor": "#007bff",
- "secondaryColor": "#6c757d",
- "fontFamily": "Arial, sans-serif"
- },
- "features": {
- "darkMode": true,
- "notifications": true,
- "analytics": false
- },
- "version": "1.0.0"
-}
-
-
-
-## ask_followup_question
-Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively.
-
-Parameters:
-- question: (required) A clear, specific question addressing the information needed
-- follow_up: (required) A list of 2-4 suggested answers, each in its own tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
-
-Usage:
-
-Your question here
-
-First suggestion
-Action with mode switch
-
-
-
-Example:
-
-What is the path to the frontend-config.json file?
-
-./src/frontend-config.json
-./config/frontend-config.json
-./frontend-config.json
-
-
-
-## attempt_completion
-Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
-IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.
-Parameters:
-- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.
-Usage:
-
-
-Your final result description here
-
-
-
-Example: Requesting to attempt completion with a result
-
-
-I've updated the CSS
-
-
-
-## switch_mode
-Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch.
-Parameters:
-- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect")
-- reason: (optional) The reason for switching modes
-Usage:
-
-Mode slug here
-Reason for switching here
-
-
-Example: Requesting to switch to code mode
-
-code
-Need to make code changes
-
-
-## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-
-Example:
-
-code
-Implement a new feature for the application
-
-
-
-## update_todo_list
-
-**Description:**
-Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks.
-
-**Checklist Format:**
-- Use a single-level markdown checklist (no nesting or subtasks).
-- List todos in the intended execution order.
-- Status options:
- - [ ] Task description (pending)
- - [x] Task description (completed)
- - [-] Task description (in progress)
-
-**Status Rules:**
-- [ ] = pending (not started)
-- [x] = completed (fully finished, no unresolved issues)
-- [-] = in_progress (currently being worked on)
-
-**Core Principles:**
-- Before updating, always confirm which todos have been completed since the last update.
-- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress).
-- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately.
-- Do not remove any unfinished todos unless explicitly instructed.
-- Always retain all unfinished tasks, updating their status as needed.
-- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies).
-- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved.
-- Remove tasks only if they are no longer relevant or if the user requests deletion.
-
-**Usage Example:**
-
-
-[x] Analyze requirements
-[x] Design architecture
-[-] Implement core logic
-[ ] Write tests
-[ ] Update documentation
-
-
-
-*After completing "Implement core logic" and starting "Write tests":*
-
-
-[x] Analyze requirements
-[x] Design architecture
-[x] Implement core logic
-[-] Write tests
-[ ] Update documentation
-[ ] Add performance benchmarks
-
-
-
-**When to Use:**
-- The task is complicated or involves multiple steps or requires ongoing tracking.
-- You need to update the status of several todos at once.
-- New actionable items are discovered during task execution.
-- The user requests a todo list or provides multiple tasks.
-- The task is complex and benefits from clear, stepwise progress tracking.
-
-**When NOT to Use:**
-- There is only a single, trivial task.
-- The task can be completed in one or two simple steps.
-- The request is purely conversational or informational.
-
-**Task Management Guidelines:**
-- Mark task as completed immediately after all work of the current task is done.
-- Start the next task by marking it as in_progress.
-- Add new todos as soon as they are identified.
-- Use clear, descriptive task names.
-
-
-# Tool Use Guidelines
+ # Tool Use Guidelines
1. Assess what information you already have and what information you need to proceed with the task.
2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like `ls` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.
3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.
-4. Formulate your tool use using the XML format specified for each tool.
-5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
+4. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
- Information about whether the tool succeeded or failed, along with any reasons for failure.
- Linter errors that may have arisen due to the changes you made, which you'll need to address.
- New terminal output in reaction to the changes, which you may need to consider or act upon.
- Any other relevant feedback or information related to the tool use.
-6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
+5. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.
It is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:
1. Confirm the success of each step before proceeding.
@@ -385,7 +53,7 @@ MODES
RULES
- The project base directory is: /test/path
-- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to .
+- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to execute_command.
- You cannot `cd` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path.
- Do not use the ~ character or $HOME to refer to the home directory.
- 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 '/test/path', and if so prepend with `cd`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run `npm install` in a project outside of '/test/path', 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)`.
@@ -434,7 +102,7 @@ You accomplish a given task iteratively, breaking it down into clear steps and w
USER'S CUSTOM INSTRUCTIONS
-The following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the TOOL USE guidelines.
+The following additional instructions are provided by the user, and should be followed to the best of your ability.
Language Preference:
You should always speak and think in the "en" language.
diff --git a/src/core/prompts/__tests__/responses-rooignore.spec.ts b/src/core/prompts/__tests__/responses-rooignore.spec.ts
index ca0dcfbad5..03aae96776 100644
--- a/src/core/prompts/__tests__/responses-rooignore.spec.ts
+++ b/src/core/prompts/__tests__/responses-rooignore.spec.ts
@@ -51,10 +51,13 @@ describe("RooIgnore Response Formatting", () => {
it("should format error message for ignored files", () => {
const errorMessage = formatResponse.rooIgnoreError("secrets/api-keys.json")
- // Verify error message format
- expect(errorMessage).toContain("Access to secrets/api-keys.json is blocked by the .rooignore file settings")
- expect(errorMessage).toContain("continue in the task without using this file")
- expect(errorMessage).toContain("ask the user to update the .rooignore file")
+ // Verify error message format (JSON)
+ const parsed = JSON.parse(errorMessage) as any
+ expect(parsed.status).toBe("error")
+ expect(parsed.type).toBe("access_denied")
+ expect(parsed.path).toBe("secrets/api-keys.json")
+ expect(parsed.suggestion).toContain("continue without this file")
+ expect(parsed.suggestion).toContain("update the .rooignore file")
})
/**
@@ -66,7 +69,8 @@ describe("RooIgnore Response Formatting", () => {
// Test each path
for (const testPath of paths) {
const errorMessage = formatResponse.rooIgnoreError(testPath)
- expect(errorMessage).toContain(`Access to ${testPath} is blocked`)
+ const parsed = JSON.parse(errorMessage) as any
+ expect(parsed.path).toBe(testPath)
}
})
})
diff --git a/src/core/prompts/__tests__/system-prompt.spec.ts b/src/core/prompts/__tests__/system-prompt.spec.ts
index a0953b2de1..7964102929 100644
--- a/src/core/prompts/__tests__/system-prompt.spec.ts
+++ b/src/core/prompts/__tests__/system-prompt.spec.ts
@@ -112,9 +112,7 @@ __setMockImplementation(
}
const joinedSections = sections.join("\n\n")
- const effectiveProtocol = options?.settings?.toolProtocol || "xml"
- const skipXmlReferences = effectiveProtocol === "native"
- const toolUseRef = skipXmlReferences ? "." : " without interfering with the TOOL USE guidelines."
+ const toolUseRef = "."
return joinedSections
? `\n====\n\nUSER'S CUSTOM INSTRUCTIONS\n\nThe following additional instructions are provided by the user, and should be followed to the best of your ability${toolUseRef}\n\n${joinedSections}`
: ""
@@ -352,7 +350,9 @@ describe("SYSTEM_PROMPT", () => {
undefined, // partialReadsEnabled
)
- expect(prompt).toContain("apply_diff")
+ // Native-only: tool catalog isn't embedded in the system prompt anymore.
+ expect(prompt).not.toContain("# Tools")
+ expect(prompt).not.toContain("apply_diff")
expect(prompt).toMatchFileSnapshot("./__snapshots__/system-prompt/with-diff-enabled-true.snap")
})
@@ -376,6 +376,8 @@ describe("SYSTEM_PROMPT", () => {
undefined, // partialReadsEnabled
)
+ // Native-only: tool catalog isn't embedded in the system prompt anymore.
+ expect(prompt).not.toContain("# Tools")
expect(prompt).not.toContain("apply_diff")
expect(prompt).toMatchFileSnapshot("./__snapshots__/system-prompt/with-diff-enabled-false.snap")
})
@@ -400,6 +402,8 @@ describe("SYSTEM_PROMPT", () => {
undefined, // partialReadsEnabled
)
+ // Native-only: tool catalog isn't embedded in the system prompt anymore.
+ expect(prompt).not.toContain("# Tools")
expect(prompt).not.toContain("apply_diff")
expect(prompt).toMatchFileSnapshot("./__snapshots__/system-prompt/with-diff-enabled-undefined.snap")
})
@@ -593,7 +597,6 @@ describe("SYSTEM_PROMPT", () => {
todoListEnabled: false,
useAgentRules: true,
newTaskRequireTodos: false,
- toolProtocol: "xml" as const,
}
const prompt = await SYSTEM_PROMPT(
@@ -627,7 +630,6 @@ describe("SYSTEM_PROMPT", () => {
todoListEnabled: true,
useAgentRules: true,
newTaskRequireTodos: false,
- toolProtocol: "xml" as const,
}
const prompt = await SYSTEM_PROMPT(
@@ -650,8 +652,9 @@ describe("SYSTEM_PROMPT", () => {
settings, // settings
)
+ // update_todo_list is still referenced by mode instructions, but tool catalogs are not embedded.
expect(prompt).toContain("update_todo_list")
- expect(prompt).toContain("## update_todo_list")
+ expect(prompt).not.toContain("## update_todo_list")
})
it("should include update_todo_list tool when todoListEnabled is undefined", async () => {
@@ -660,7 +663,6 @@ describe("SYSTEM_PROMPT", () => {
todoListEnabled: true,
useAgentRules: true,
newTaskRequireTodos: false,
- toolProtocol: "xml" as const,
}
const prompt = await SYSTEM_PROMPT(
@@ -683,89 +685,17 @@ describe("SYSTEM_PROMPT", () => {
settings, // settings
)
+ // update_todo_list is still referenced by mode instructions, but tool catalogs are not embedded.
expect(prompt).toContain("update_todo_list")
- expect(prompt).toContain("## update_todo_list")
+ expect(prompt).not.toContain("## update_todo_list")
})
- it("should include XML tool instructions when disableXmlToolInstructions is false (default)", async () => {
+ it("should include native tool instructions", async () => {
const settings = {
maxConcurrentFileReads: 5,
todoListEnabled: true,
useAgentRules: true,
newTaskRequireTodos: false,
- toolProtocol: "xml" as const, // explicitly xml
- }
-
- const prompt = await SYSTEM_PROMPT(
- mockContext,
- "/test/path",
- false,
- undefined, // mcpHub
- undefined, // diffStrategy
- undefined, // browserViewportSize
- defaultModeSlug, // mode
- undefined, // customModePrompts
- undefined, // customModes
- undefined, // globalCustomInstructions
- undefined, // diffEnabled
- experiments,
- true, // enableMcpServerCreation
- undefined, // language
- undefined, // rooIgnoreInstructions
- undefined, // partialReadsEnabled
- settings, // settings
- )
-
- // Should contain XML guidance sections
- expect(prompt).toContain("TOOL USE")
- expect(prompt).toContain("XML-style tags")
- expect(prompt).toContain("")
- expect(prompt).toContain("")
- expect(prompt).toContain("Tool Use Guidelines")
- expect(prompt).toContain("# Tools")
-
- // Should contain tool descriptions with XML examples
- expect(prompt).toContain("## read_file")
- expect(prompt).toContain("")
- expect(prompt).toContain("")
-
- // Should be byte-for-byte compatible with default behavior
- const defaultPrompt = await SYSTEM_PROMPT(
- mockContext,
- "/test/path",
- false,
- undefined,
- undefined,
- undefined,
- defaultModeSlug,
- undefined,
- undefined,
- undefined,
- undefined,
- experiments,
- true,
- undefined,
- undefined,
- undefined,
- {
- maxConcurrentFileReads: 5,
- todoListEnabled: true,
- useAgentRules: true,
- newTaskRequireTodos: false,
- toolProtocol: "xml" as const,
- },
- )
-
- expect(prompt).toBe(defaultPrompt)
- })
-
- it("should include native tool instructions when toolProtocol is native", async () => {
- const settings = {
- maxConcurrentFileReads: 5,
- todoListEnabled: true,
- useAgentRules: true,
- newTaskRequireTodos: false,
- toolProtocol: "native" as const, // native protocol
}
const prompt = await SYSTEM_PROMPT(
@@ -794,17 +724,13 @@ describe("SYSTEM_PROMPT", () => {
expect(prompt).toContain("Do not include XML markup or examples")
// Should NOT contain XML-style tags or examples
- expect(prompt).not.toContain("XML-style tags")
expect(prompt).not.toContain("")
expect(prompt).not.toContain("")
- // Should contain Tool Use Guidelines section without format-specific guidance
+ // Should contain Tool Use Guidelines section
expect(prompt).toContain("Tool Use Guidelines")
- // Should NOT contain any protocol-specific formatting instructions
- expect(prompt).not.toContain("provider's native tool-calling mechanism")
- expect(prompt).not.toContain("XML format specified for each tool")
- // Should NOT contain # Tools catalog at all in native mode
+ // Should NOT contain a tool catalog / XML examples
expect(prompt).not.toContain("# Tools")
expect(prompt).not.toContain("## read_file")
expect(prompt).not.toContain("## execute_command")
@@ -821,43 +747,6 @@ describe("SYSTEM_PROMPT", () => {
expect(prompt).toContain("OBJECTIVE")
})
- it("should default to XML tool instructions when toolProtocol is undefined", async () => {
- const settings = {
- maxConcurrentFileReads: 5,
- todoListEnabled: true,
- useAgentRules: true,
- newTaskRequireTodos: false,
- toolProtocol: "xml" as const,
- }
-
- const prompt = await SYSTEM_PROMPT(
- mockContext,
- "/test/path",
- false,
- undefined, // mcpHub
- undefined, // diffStrategy
- undefined, // browserViewportSize
- defaultModeSlug, // mode
- undefined, // customModePrompts
- undefined, // customModes
- undefined, // globalCustomInstructions
- undefined, // diffEnabled
- experiments,
- true, // enableMcpServerCreation
- undefined, // language
- undefined, // rooIgnoreInstructions
- undefined, // partialReadsEnabled
- settings, // settings
- )
-
- // Should contain XML guidance (default behavior)
- expect(prompt).toContain("TOOL USE")
- expect(prompt).toContain("XML-style tags")
- expect(prompt).toContain("")
- expect(prompt).toContain("Tool Use Guidelines")
- expect(prompt).toContain("# Tools")
- })
-
afterAll(() => {
vi.restoreAllMocks()
})
diff --git a/src/core/prompts/instructions/create-mode.ts b/src/core/prompts/instructions/create-mode.ts
index 80f69b0802..9623aae0cd 100644
--- a/src/core/prompts/instructions/create-mode.ts
+++ b/src/core/prompts/instructions/create-mode.ts
@@ -17,7 +17,6 @@ Custom modes can be configured in two ways:
When modes with the same slug exist in both files, the workspace-specific .roomodes version takes precedence. This allows projects to override global modes or define project-specific modes.
-
If asked to create a project mode, create it in .roomodes in the workspace root. If asked to create a global mode, use the global custom modes file.
- The following fields are required and must not be empty:
diff --git a/src/core/prompts/responses.ts b/src/core/prompts/responses.ts
index 332e3c63b7..60b5b4123a 100644
--- a/src/core/prompts/responses.ts
+++ b/src/core/prompts/responses.ts
@@ -3,65 +3,44 @@ 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, TOOL_PROTOCOL } from "@roo-code/types"
export const formatResponse = {
- toolDenied: (protocol?: ToolProtocol) => {
- if (isNativeProtocol(protocol ?? TOOL_PROTOCOL.XML)) {
- return JSON.stringify({
- status: "denied",
- message: "The user denied this operation.",
- })
- }
- return `The user denied this operation.`
- },
+ toolDenied: () =>
+ JSON.stringify({
+ status: "denied",
+ message: "The user denied this operation.",
+ }),
- toolDeniedWithFeedback: (feedback?: string, protocol?: ToolProtocol) => {
- if (isNativeProtocol(protocol ?? TOOL_PROTOCOL.XML)) {
- return JSON.stringify({
- status: "denied",
- feedback: feedback,
- })
- }
- return `The user denied this operation and responded with the message:\n\n${feedback}\n`
- },
+ toolDeniedWithFeedback: (feedback?: string) =>
+ JSON.stringify({
+ status: "denied",
+ feedback,
+ }),
- toolApprovedWithFeedback: (feedback?: string, protocol?: ToolProtocol) => {
- if (isNativeProtocol(protocol ?? TOOL_PROTOCOL.XML)) {
- return JSON.stringify({
- status: "approved",
- feedback: feedback,
- })
- }
- return `The user approved this operation and responded with the message:\n\n${feedback}\n`
- },
+ toolApprovedWithFeedback: (feedback?: string) =>
+ JSON.stringify({
+ status: "approved",
+ feedback,
+ }),
- toolError: (error?: string, protocol?: ToolProtocol) => {
- if (isNativeProtocol(protocol ?? TOOL_PROTOCOL.XML)) {
- return JSON.stringify({
- status: "error",
- message: "The tool execution failed",
- error: error,
- })
- }
- return `The tool execution failed with the following error:\n\n${error}\n`
- },
+ toolError: (error?: string) =>
+ JSON.stringify({
+ status: "error",
+ message: "The tool execution failed",
+ error,
+ }),
- rooIgnoreError: (path: string, protocol?: ToolProtocol) => {
- if (isNativeProtocol(protocol ?? TOOL_PROTOCOL.XML)) {
- return JSON.stringify({
- status: "error",
- type: "access_denied",
- message: "Access blocked by .rooignore",
- path: path,
- suggestion: "Try to continue without this file, or ask the user to update the .rooignore file",
- })
- }
- return `Access to ${path} is blocked by the .rooignore file settings. You must try to continue in the task without using this file, or ask the user to update the .rooignore file.`
- },
+ rooIgnoreError: (path: string) =>
+ JSON.stringify({
+ status: "error",
+ type: "access_denied",
+ message: "Access blocked by .rooignore",
+ path,
+ suggestion: "Try to continue without this file, or ask the user to update the .rooignore file",
+ }),
- noToolsUsed: (protocol?: ToolProtocol) => {
- const instructions = getToolInstructionsReminder(protocol)
+ noToolsUsed: () => {
+ const instructions = getToolInstructionsReminder()
return `[ERROR] You did not use a tool in your previous response! Please retry with a tool use.
@@ -75,65 +54,47 @@ Otherwise, if you have not completed the task and do not need additional informa
(This is an automated message, so do not respond to it conversationally.)`
},
- tooManyMistakes: (feedback?: string, protocol?: ToolProtocol) => {
- if (isNativeProtocol(protocol ?? TOOL_PROTOCOL.XML)) {
- return JSON.stringify({
- status: "guidance",
- feedback: feedback,
- })
- }
- return `You seem to be having trouble proceeding. The user has provided the following feedback to help guide you:\n\n${feedback}\n`
- },
+ tooManyMistakes: (feedback?: string) =>
+ JSON.stringify({
+ status: "guidance",
+ feedback,
+ }),
- missingToolParameterError: (paramName: string, protocol?: ToolProtocol) => {
- const instructions = getToolInstructionsReminder(protocol)
+ missingToolParameterError: (paramName: string) => {
+ const instructions = getToolInstructionsReminder()
return `Missing value for required parameter '${paramName}'. Please retry with complete response.\n\n${instructions}`
},
- invalidMcpToolArgumentError: (serverName: string, toolName: string, protocol?: ToolProtocol) => {
- if (isNativeProtocol(protocol ?? TOOL_PROTOCOL.XML)) {
- return JSON.stringify({
- status: "error",
- type: "invalid_argument",
- message: "Invalid JSON argument",
- server: serverName,
- tool: toolName,
- suggestion: "Please retry with a properly formatted JSON argument",
- })
- }
- return `Invalid JSON argument used with ${serverName} for ${toolName}. Please retry with a properly formatted JSON argument.`
- },
+ invalidMcpToolArgumentError: (serverName: string, toolName: string) =>
+ JSON.stringify({
+ status: "error",
+ type: "invalid_argument",
+ message: "Invalid JSON argument",
+ server: serverName,
+ tool: toolName,
+ suggestion: "Please retry with a properly formatted JSON argument",
+ }),
- unknownMcpToolError: (serverName: string, toolName: string, availableTools: string[], protocol?: ToolProtocol) => {
- if (isNativeProtocol(protocol ?? TOOL_PROTOCOL.XML)) {
- return JSON.stringify({
- status: "error",
- type: "unknown_tool",
- message: "Tool does not exist on server",
- server: serverName,
- tool: toolName,
- available_tools: availableTools.length > 0 ? availableTools : [],
- suggestion: "Please use one of the available tools or check if the server is properly configured",
- })
- }
- const toolsList = availableTools.length > 0 ? availableTools.join(", ") : "No tools available"
- return `Tool '${toolName}' does not exist on server '${serverName}'.\n\nAvailable tools on this server: ${toolsList}\n\nPlease use one of the available tools or check if the server is properly configured.`
- },
+ unknownMcpToolError: (serverName: string, toolName: string, availableTools: string[]) =>
+ JSON.stringify({
+ status: "error",
+ type: "unknown_tool",
+ message: "Tool does not exist on server",
+ server: serverName,
+ tool: toolName,
+ available_tools: availableTools.length > 0 ? availableTools : [],
+ suggestion: "Please use one of the available tools or check if the server is properly configured",
+ }),
- unknownMcpServerError: (serverName: string, availableServers: string[], protocol?: ToolProtocol) => {
- if (isNativeProtocol(protocol ?? TOOL_PROTOCOL.XML)) {
- return JSON.stringify({
- status: "error",
- type: "unknown_server",
- message: "Server is not configured",
- server: serverName,
- available_servers: availableServers.length > 0 ? availableServers : [],
- })
- }
- const serversList = availableServers.length > 0 ? availableServers.join(", ") : "No servers available"
- return `Server '${serverName}' is not configured. Available servers: ${serversList}`
- },
+ unknownMcpServerError: (serverName: string, availableServers: string[]) =>
+ JSON.stringify({
+ status: "error",
+ type: "unknown_server",
+ message: "Server is not configured",
+ server: serverName,
+ available_servers: availableServers.length > 0 ? availableServers : [],
+ }),
toolResult: (
text: string,
@@ -255,26 +216,6 @@ const formatImagesIntoBlocks = (images?: string[]): Anthropic.ImageBlockParam[]
: []
}
-const toolUseInstructionsReminder = `# Reminder: Instructions for Tool Use
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-For example, to use the attempt_completion tool:
-
-
-
-I have completed the task...
-
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.`
-
const toolUseInstructionsReminderNative = `# Reminder: Instructions for Tool Use
Tools are invoked using the platform's native tool calling mechanism. Each tool requires specific parameters as defined in the tool descriptions. Refer to the tool definitions provided in your system instructions for the correct parameter structure and usage examples.
@@ -282,12 +223,8 @@ Tools are invoked using the platform's native tool calling mechanism. Each tool
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, defaults to XML if not provided
- * @returns The tool use instructions reminder text
+ * Gets the tool use instructions reminder.
*/
-function getToolInstructionsReminder(protocol?: ToolProtocol): string {
- const effectiveProtocol = protocol ?? TOOL_PROTOCOL.XML
- return isNativeProtocol(effectiveProtocol) ? toolUseInstructionsReminderNative : toolUseInstructionsReminder
+function getToolInstructionsReminder(): string {
+ return toolUseInstructionsReminderNative
}
diff --git a/src/core/prompts/sections/__tests__/tool-use-guidelines.spec.ts b/src/core/prompts/sections/__tests__/tool-use-guidelines.spec.ts
index 3cb3fb51d0..26c1f77d85 100644
--- a/src/core/prompts/sections/__tests__/tool-use-guidelines.spec.ts
+++ b/src/core/prompts/sections/__tests__/tool-use-guidelines.spec.ts
@@ -1,99 +1,66 @@
import { getToolUseGuidelinesSection } from "../tool-use-guidelines"
-import { TOOL_PROTOCOL } from "@roo-code/types"
import { EXPERIMENT_IDS } from "../../../../shared/experiments"
describe("getToolUseGuidelinesSection", () => {
- describe("XML protocol", () => {
+ describe("with MULTIPLE_NATIVE_TOOL_CALLS disabled (default)", () => {
it("should include proper numbered guidelines", () => {
- const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.XML)
+ const guidelines = getToolUseGuidelinesSection()
// Check that all numbered items are present with correct numbering
expect(guidelines).toContain("1. Assess what information")
expect(guidelines).toContain("2. Choose the most appropriate tool")
expect(guidelines).toContain("3. If multiple actions are needed")
- expect(guidelines).toContain("4. Formulate your tool use")
- expect(guidelines).toContain("5. After each tool use")
- expect(guidelines).toContain("6. ALWAYS wait for user confirmation")
+ expect(guidelines).toContain("4. After each tool use")
})
- it("should include XML-specific guidelines", () => {
- const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.XML)
+ it("should include single-tool-per-message guidance when experiment disabled", () => {
+ const guidelines = getToolUseGuidelinesSection({})
- expect(guidelines).toContain("Formulate your tool use using the XML format specified for each tool")
expect(guidelines).toContain("use one tool at a time per message")
+ expect(guidelines).not.toContain("you may use multiple tools in a single message")
+ expect(guidelines).not.toContain("Formulate your tool use using")
expect(guidelines).toContain("ALWAYS wait for user confirmation")
})
- it("should include iterative process guidelines", () => {
- const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.XML)
+ it("should include simplified iterative process guidelines", () => {
+ const guidelines = getToolUseGuidelinesSection()
+ expect(guidelines).toContain("carefully considering the user's response after each tool use")
expect(guidelines).toContain("It is crucial to proceed step-by-step")
- expect(guidelines).toContain("1. Confirm the success of each step before proceeding")
- expect(guidelines).toContain("2. Address any issues or errors that arise immediately")
- expect(guidelines).toContain("3. Adapt your approach based on new information")
- expect(guidelines).toContain("4. Ensure that each action builds correctly")
})
})
- describe("native protocol", () => {
- describe("with MULTIPLE_NATIVE_TOOL_CALLS disabled (default)", () => {
- it("should include proper numbered guidelines", () => {
- const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.NATIVE)
-
- // Check that all numbered items are present with correct numbering
- expect(guidelines).toContain("1. Assess what information")
- expect(guidelines).toContain("2. Choose the most appropriate tool")
- expect(guidelines).toContain("3. If multiple actions are needed")
- expect(guidelines).toContain("4. After each tool use")
+ describe("with MULTIPLE_NATIVE_TOOL_CALLS enabled", () => {
+ it("should include multiple-tools-per-message guidance when experiment enabled", () => {
+ const guidelines = getToolUseGuidelinesSection({
+ [EXPERIMENT_IDS.MULTIPLE_NATIVE_TOOL_CALLS]: true,
})
- it("should include single-tool-per-message guidance when experiment disabled", () => {
- const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.NATIVE, {})
-
- expect(guidelines).toContain("use one tool at a time per message")
- expect(guidelines).not.toContain("you may use multiple tools in a single message")
- expect(guidelines).not.toContain("Formulate your tool use using the XML format")
- expect(guidelines).not.toContain("ALWAYS wait for user confirmation")
- })
-
- it("should include simplified iterative process guidelines", () => {
- const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.NATIVE)
-
- expect(guidelines).toContain("carefully considering the user's response after tool executions")
- // Native protocol doesn't have the step-by-step list
- expect(guidelines).not.toContain("It is crucial to proceed step-by-step")
- })
+ expect(guidelines).toContain("you may use multiple tools in a single message")
+ expect(guidelines).not.toContain("use one tool at a time per message")
+ expect(guidelines).not.toContain("After each tool use, the user will respond")
})
- describe("with MULTIPLE_NATIVE_TOOL_CALLS enabled", () => {
- it("should include multiple-tools-per-message guidance when experiment enabled", () => {
- const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.NATIVE, {
- [EXPERIMENT_IDS.MULTIPLE_NATIVE_TOOL_CALLS]: true,
- })
-
- expect(guidelines).toContain("you may use multiple tools in a single message")
- expect(guidelines).not.toContain("use one tool at a time per message")
+ it("should use simplified footer without step-by-step language", () => {
+ const guidelines = getToolUseGuidelinesSection({
+ [EXPERIMENT_IDS.MULTIPLE_NATIVE_TOOL_CALLS]: true,
})
- it("should include simplified iterative process guidelines", () => {
- const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.NATIVE, {
- [EXPERIMENT_IDS.MULTIPLE_NATIVE_TOOL_CALLS]: true,
- })
-
- expect(guidelines).toContain("carefully considering the user's response after tool executions")
- expect(guidelines).not.toContain("It is crucial to proceed step-by-step")
- })
+ // When multiple tools per message is enabled, we don't want the
+ // "step-by-step" or "after each tool use" language that would
+ // contradict the ability to batch tool calls.
+ expect(guidelines).toContain("carefully considering the user's response after tool executions")
+ expect(guidelines).not.toContain("It is crucial to proceed step-by-step")
+ expect(guidelines).not.toContain("ALWAYS wait for user confirmation after each tool use")
})
})
- it("should include common guidance regardless of protocol", () => {
- const guidelinesXml = getToolUseGuidelinesSection(TOOL_PROTOCOL.XML)
- const guidelinesNative = getToolUseGuidelinesSection(TOOL_PROTOCOL.NATIVE)
-
- for (const guidelines of [guidelinesXml, guidelinesNative]) {
- expect(guidelines).toContain("Assess what information you already have")
- expect(guidelines).toContain("Choose the most appropriate tool")
- expect(guidelines).toContain("After each tool use, the user will respond")
- }
+ it("should include common guidance", () => {
+ const guidelines = getToolUseGuidelinesSection()
+ expect(guidelines).toContain("Assess what information you already have")
+ expect(guidelines).toContain("Choose the most appropriate tool")
+ expect(guidelines).toContain("After each tool use, the user will respond")
+ // No legacy XML-tag tool-calling remnants
+ expect(guidelines).not.toContain("")
})
})
diff --git a/src/core/prompts/sections/__tests__/tool-use.spec.ts b/src/core/prompts/sections/__tests__/tool-use.spec.ts
index c8e3a9b5d0..1d945612ea 100644
--- a/src/core/prompts/sections/__tests__/tool-use.spec.ts
+++ b/src/core/prompts/sections/__tests__/tool-use.spec.ts
@@ -1,41 +1,24 @@
import { getSharedToolUseSection } from "../tool-use"
-import { TOOL_PROTOCOL } from "@roo-code/types"
describe("getSharedToolUseSection", () => {
- describe("XML protocol", () => {
- it("should include one tool per message requirement", () => {
- const section = getSharedToolUseSection(TOOL_PROTOCOL.XML)
-
- expect(section).toContain("You must use exactly one tool per message")
- expect(section).toContain("every assistant message must include a tool call")
- })
-
- it("should include XML formatting instructions", () => {
- const section = getSharedToolUseSection(TOOL_PROTOCOL.XML)
-
- expect(section).toContain("XML-style tags")
- expect(section).toContain("Always use the actual tool name as the XML tag name")
- })
- })
-
- describe("native protocol", () => {
+ describe("native tool calling", () => {
it("should include one tool per message requirement when experiment is disabled", () => {
// No experiment flags passed (default: disabled)
- const section = getSharedToolUseSection(TOOL_PROTOCOL.NATIVE)
+ const section = getSharedToolUseSection()
expect(section).toContain("You must use exactly one tool call per assistant response")
expect(section).toContain("Do not call zero tools or more than one tool")
})
it("should include one tool per message requirement when experiment is explicitly disabled", () => {
- const section = getSharedToolUseSection(TOOL_PROTOCOL.NATIVE, { multipleNativeToolCalls: false })
+ const section = getSharedToolUseSection({ multipleNativeToolCalls: false })
expect(section).toContain("You must use exactly one tool call per assistant response")
expect(section).toContain("Do not call zero tools or more than one tool")
})
it("should NOT include one tool per message requirement when experiment is enabled", () => {
- const section = getSharedToolUseSection(TOOL_PROTOCOL.NATIVE, { multipleNativeToolCalls: true })
+ const section = getSharedToolUseSection({ multipleNativeToolCalls: true })
expect(section).not.toContain("You must use exactly one tool per message")
expect(section).not.toContain("every assistant message must include a tool call")
@@ -44,26 +27,26 @@ describe("getSharedToolUseSection", () => {
})
it("should include native tool-calling instructions", () => {
- const section = getSharedToolUseSection(TOOL_PROTOCOL.NATIVE)
+ const section = getSharedToolUseSection()
expect(section).toContain("provider-native tool-calling mechanism")
expect(section).toContain("Do not include XML markup or examples")
})
it("should NOT include XML formatting instructions", () => {
- const section = getSharedToolUseSection(TOOL_PROTOCOL.NATIVE)
+ const section = getSharedToolUseSection()
- expect(section).not.toContain("XML-style tags")
- expect(section).not.toContain("Always use the actual tool name as the XML tag name")
+ expect(section).not.toContain("")
+ expect(section).not.toContain("")
})
})
- describe("default protocol", () => {
- it("should default to XML protocol when no protocol is specified", () => {
+ describe("default (native-only)", () => {
+ it("should default to native tool calling when no arguments are provided", () => {
const section = getSharedToolUseSection()
-
- expect(section).toContain("XML-style tags")
- expect(section).toContain("You must use exactly one tool per message")
+ expect(section).toContain("provider-native tool-calling mechanism")
+ // No legacy XML-tag tool-calling remnants
+ expect(section).not.toContain("")
})
})
})
diff --git a/src/core/prompts/sections/custom-instructions.ts b/src/core/prompts/sections/custom-instructions.ts
index ed33f4a1e3..8eee0a0998 100644
--- a/src/core/prompts/sections/custom-instructions.ts
+++ b/src/core/prompts/sections/custom-instructions.ts
@@ -6,7 +6,6 @@ import { Dirent } from "fs"
import { isLanguage } from "@roo-code/types"
import type { SystemPromptSettings } from "../types"
-import { getEffectiveProtocol, isNativeProtocol } from "@roo-code/types"
import { LANGUAGES } from "../../../shared/language"
import {
@@ -459,17 +458,13 @@ export async function addCustomInstructions(
const joinedSections = sections.join("\n\n")
- const effectiveProtocol = getEffectiveProtocol(options.settings?.toolProtocol)
-
return joinedSections
? `
====
USER'S CUSTOM INSTRUCTIONS
-The following additional instructions are provided by the user, and should be followed to the best of your ability${
- isNativeProtocol(effectiveProtocol) ? "." : " without interfering with the TOOL USE guidelines."
- }
+The following additional instructions are provided by the user, and should be followed to the best of your ability.
${joinedSections}
`
diff --git a/src/core/prompts/sections/mcp-servers.ts b/src/core/prompts/sections/mcp-servers.ts
index 3eb1569c5a..42a6d5d440 100644
--- a/src/core/prompts/sections/mcp-servers.ts
+++ b/src/core/prompts/sections/mcp-servers.ts
@@ -17,7 +17,6 @@ export async function getMcpServersSection(
.getServers()
.filter((server) => server.status === "connected")
.map((server) => {
- // Only include tool descriptions when using XML protocol
const tools = includeToolDescriptions
? server.tools
?.filter((tool) => tool.enabledForPrompt !== false)
@@ -56,7 +55,7 @@ export async function getMcpServersSection(
// Different instructions based on protocol
const toolAccessInstructions = includeToolDescriptions
? `When a server is connected, you can use the server's tools via the \`use_mcp_tool\` tool, and access the server's resources via the \`access_mcp_resource\` tool.`
- : `When a server is connected, each server's tools are available as native tools with the naming pattern \`mcp_{server_name}_{tool_name}\`. For example, a tool named 'get_forecast' from a server named 'weather' would be available as \`mcp_weather_get_forecast\`. You can also access server resources using the \`access_mcp_resource\` tool.`
+ : `When a server is connected, you can use the server's tools via the \`use_mcp_tool\` tool, and access the server's resources via the \`access_mcp_resource\` tool.`
const baseSection = `MCP SERVERS
diff --git a/src/core/prompts/sections/rules.ts b/src/core/prompts/sections/rules.ts
index 800fb430ef..4f6e573fa7 100644
--- a/src/core/prompts/sections/rules.ts
+++ b/src/core/prompts/sections/rules.ts
@@ -1,5 +1,4 @@
import type { SystemPromptSettings } from "../types"
-import { getEffectiveProtocol, isNativeProtocol } from "@roo-code/types"
import { getShell } from "../../../utils/shell"
@@ -64,9 +63,6 @@ When asked about your creator, vendor, or company, respond with:
}
export function getRulesSection(cwd: string, settings?: SystemPromptSettings): string {
- // Determine whether to use XML tool references based on protocol
- const effectiveProtocol = getEffectiveProtocol(settings?.toolProtocol)
-
// Get shell-appropriate command chaining operator
const chainOp = getCommandChainOperator()
const chainNote = getCommandChainNote()
@@ -76,7 +72,7 @@ export function getRulesSection(cwd: string, settings?: SystemPromptSettings): s
RULES
- The project base directory is: ${cwd.toPosix()}
-- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to ${isNativeProtocol(effectiveProtocol) ? "execute_command" : ""}.
+- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to execute_command.
- You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '${cwd.toPosix()}', so be sure to pass in the correct 'path' parameter when using tools that require a path.
- Do not use the ~ character or $HOME to refer to the home directory.
- 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 ${chainOp} 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) ${chainOp} (command, in this case npm install)\`.${chainNote ? ` ${chainNote}` : ""}
diff --git a/src/core/prompts/sections/skills.ts b/src/core/prompts/sections/skills.ts
index 954d0451bd..53ba8b95f1 100644
--- a/src/core/prompts/sections/skills.ts
+++ b/src/core/prompts/sections/skills.ts
@@ -80,6 +80,16 @@ CONSTRAINTS:
- FAILURE to perform this check is an error.
+
+- When a SKILL.md is loaded, ONLY the contents of SKILL.md are present.
+- Files linked from SKILL.md are NOT loaded automatically.
+- The model MUST explicitly decide to read a linked file based on task relevance.
+- Do NOT assume the contents of linked files unless they have been explicitly read.
+- Prefer reading the minimum necessary linked file.
+- Avoid reading multiple linked files unless required.
+- Treat linked files as progressive disclosure, not mandatory context.
+
+
- The skill list is already filtered for the current mode: "${currentMode}".
- Mode-specific skills may come from skills-${currentMode}/ with project-level overrides taking precedence over global skills.
diff --git a/src/core/prompts/sections/tool-use-guidelines.ts b/src/core/prompts/sections/tool-use-guidelines.ts
index a5dad2cc0b..256e7aba5a 100644
--- a/src/core/prompts/sections/tool-use-guidelines.ts
+++ b/src/core/prompts/sections/tool-use-guidelines.ts
@@ -1,12 +1,6 @@
-import { ToolProtocol, TOOL_PROTOCOL } from "@roo-code/types"
-import { isNativeProtocol } from "@roo-code/types"
-
import { experiments, EXPERIMENT_IDS } from "../../../shared/experiments"
-export function getToolUseGuidelinesSection(
- protocol: ToolProtocol = TOOL_PROTOCOL.XML,
- experimentFlags?: Record,
-): string {
+export function getToolUseGuidelinesSection(experimentFlags?: Record): string {
// Build guidelines array with automatic numbering
let itemNumber = 1
const guidelinesList: string[] = []
@@ -20,50 +14,43 @@ export function getToolUseGuidelinesSection(
`${itemNumber++}. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like \`ls\` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.`,
)
- // Remaining guidelines - different for native vs XML protocol
- if (isNativeProtocol(protocol)) {
- // Check if multiple native tool calls is enabled via experiment
- const isMultipleNativeToolCallsEnabled = experiments.isEnabled(
- experimentFlags ?? {},
- EXPERIMENT_IDS.MULTIPLE_NATIVE_TOOL_CALLS,
- )
+ // Native-only guidelines.
+ // Check if multiple native tool calls is enabled via experiment.
+ const isMultipleNativeToolCallsEnabled = experiments.isEnabled(
+ experimentFlags ?? {},
+ EXPERIMENT_IDS.MULTIPLE_NATIVE_TOOL_CALLS,
+ )
- if (isMultipleNativeToolCallsEnabled) {
- guidelinesList.push(
- `${itemNumber++}. If multiple actions are needed, you may use multiple tools in a single message when appropriate, or use tools iteratively across messages. Each tool use should be informed by the results of previous tool uses. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.`,
- )
- } else {
- guidelinesList.push(
- `${itemNumber++}. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.`,
- )
- }
+ if (isMultipleNativeToolCallsEnabled) {
+ guidelinesList.push(
+ `${itemNumber++}. If multiple actions are needed, you may use multiple tools in a single message when appropriate, or use tools iteratively across messages. Each tool use should be informed by the results of previous tool uses. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.`,
+ )
} else {
guidelinesList.push(
`${itemNumber++}. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.`,
)
}
-
- // Protocol-specific guideline - only add for XML protocol
- if (!isNativeProtocol(protocol)) {
- guidelinesList.push(`${itemNumber++}. Formulate your tool use using the XML format specified for each tool.`)
- }
- guidelinesList.push(`${itemNumber++}. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
+ // Only add the per-tool confirmation guideline when NOT using multiple tool calls.
+ // When multiple tool calls are enabled, results may arrive batched (after all tools),
+ // so "after each tool use" would contradict the batching behavior.
+ if (!isMultipleNativeToolCallsEnabled) {
+ guidelinesList.push(`${itemNumber++}. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:
- Information about whether the tool succeeded or failed, along with any reasons for failure.
- Linter errors that may have arisen due to the changes you made, which you'll need to address.
- New terminal output in reaction to the changes, which you may need to consider or act upon.
- Any other relevant feedback or information related to the tool use.`)
+ }
- // Only add the "wait for confirmation" guideline for XML protocol
- // Native protocol allows multiple tools per message, so waiting after each tool doesn't apply
- if (!isNativeProtocol(protocol)) {
+ // Only add the "wait for confirmation" guideline when NOT using multiple tool calls.
+ // With multiple tool calls enabled, the model is expected to batch tools and get results together.
+ if (!isMultipleNativeToolCallsEnabled) {
guidelinesList.push(
`${itemNumber++}. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.`,
)
}
// Join guidelines and add the footer
- // For native protocol, the footer is less relevant since multiple tools can execute in one message
- const footer = isNativeProtocol(protocol)
+ const footer = isMultipleNativeToolCallsEnabled
? `\n\nBy carefully considering the user's response after tool executions, you can react accordingly and make informed decisions about how to proceed with the task. This iterative process helps ensure the overall success and accuracy of your work.`
: `\n\nIt is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:
1. Confirm the success of each step before proceeding.
diff --git a/src/core/prompts/sections/tool-use.ts b/src/core/prompts/sections/tool-use.ts
index c3f5e221b8..4bc0905c56 100644
--- a/src/core/prompts/sections/tool-use.ts
+++ b/src/core/prompts/sections/tool-use.ts
@@ -1,44 +1,19 @@
-import { ToolProtocol, TOOL_PROTOCOL, isNativeProtocol } from "@roo-code/types"
-
import { experiments, EXPERIMENT_IDS } from "../../../shared/experiments"
-export function getSharedToolUseSection(
- protocol: ToolProtocol = TOOL_PROTOCOL.XML,
- experimentFlags?: Record,
-): string {
- if (isNativeProtocol(protocol)) {
- // Check if multiple native tool calls is enabled via experiment
- const isMultipleNativeToolCallsEnabled = experiments.isEnabled(
- experimentFlags ?? {},
- EXPERIMENT_IDS.MULTIPLE_NATIVE_TOOL_CALLS,
- )
+export function getSharedToolUseSection(experimentFlags?: Record): string {
+ // Check if multiple native tool calls is enabled via experiment
+ const isMultipleNativeToolCallsEnabled = experiments.isEnabled(
+ experimentFlags ?? {},
+ EXPERIMENT_IDS.MULTIPLE_NATIVE_TOOL_CALLS,
+ )
- const toolUseGuidance = isMultipleNativeToolCallsEnabled
- ? " You must call at least one tool per assistant response. Prefer calling as many tools as are reasonably needed in a single response to reduce back-and-forth and complete tasks faster."
- : " You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response."
-
- return `====
-
-TOOL USE
-
-You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples.${toolUseGuidance}`
- }
+ const toolUseGuidance = isMultipleNativeToolCallsEnabled
+ ? " You must call at least one tool per assistant response. Prefer calling as many tools as are reasonably needed in a single response to reduce back-and-forth and complete tasks faster."
+ : " You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response."
return `====
TOOL USE
-You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
-
-# Tool Use Formatting
-
-Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure:
-
-
-value1
-value2
-...
-
-
-Always use the actual tool name as the XML tag name for proper parsing and execution.`
+You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples.${toolUseGuidance}`
}
diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts
index 040d703929..01f99570fb 100644
--- a/src/core/prompts/system.ts
+++ b/src/core/prompts/system.ts
@@ -1,15 +1,7 @@
import * as vscode from "vscode"
import * as os from "os"
-import {
- type ModeConfig,
- type PromptComponent,
- type CustomModePrompts,
- type TodoItem,
- getEffectiveProtocol,
- isNativeProtocol,
-} from "@roo-code/types"
-import { customToolRegistry, formatXml } from "@roo-code/core"
+import { type ModeConfig, type PromptComponent, type CustomModePrompts, type TodoItem } from "@roo-code/types"
import { Mode, modes, defaultModeSlug, getModeBySlug, getGroupName, getModeSelection } from "../../shared/modes"
import { DiffStrategy } from "../../shared/tools"
@@ -23,7 +15,6 @@ import { SkillsManager } from "../../services/skills/SkillsManager"
import { PromptVariables, loadSystemPromptFile } from "./sections/custom-system-prompt"
import type { SystemPromptSettings } from "./types"
-import { getToolDescriptionsForMode } from "./tools"
import {
getRulesSection,
getSystemInfoSection,
@@ -91,60 +82,24 @@ async function generatePrompt(
const codeIndexManager = CodeIndexManager.getInstance(context, cwd)
- // Determine the effective protocol (defaults to 'xml')
- const effectiveProtocol = getEffectiveProtocol(settings?.toolProtocol)
-
const [modesSection, mcpServersSection, skillsSection] = await Promise.all([
getModesSection(context),
shouldIncludeMcp
- ? getMcpServersSection(
- mcpHub,
- effectiveDiffStrategy,
- enableMcpServerCreation,
- !isNativeProtocol(effectiveProtocol),
- )
+ ? getMcpServersSection(mcpHub, effectiveDiffStrategy, enableMcpServerCreation, false)
: Promise.resolve(""),
getSkillsSection(skillsManager, mode as string),
])
- // Build tools catalog section only for XML protocol
- const builtInToolsCatalog = isNativeProtocol(effectiveProtocol)
- ? ""
- : `\n\n${getToolDescriptionsForMode(
- mode,
- cwd,
- supportsComputerUse,
- codeIndexManager,
- effectiveDiffStrategy,
- browserViewportSize,
- shouldIncludeMcp ? mcpHub : undefined,
- customModeConfigs,
- experiments,
- partialReadsEnabled,
- settings,
- enableMcpServerCreation,
- modelId,
- )}`
-
- let customToolsSection = ""
-
- if (experiments?.customTools && !isNativeProtocol(effectiveProtocol)) {
- const customTools = customToolRegistry.getAllSerialized()
-
- if (customTools.length > 0) {
- customToolsSection = `\n\n${formatXml(customTools)}`
- }
- }
-
- const toolsCatalog = builtInToolsCatalog + customToolsSection
+ // Tools catalog is not included in the system prompt.
+ const toolsCatalog = ""
const basePrompt = `${roleDefinition}
${markdownFormattingSection()}
-${getSharedToolUseSection(effectiveProtocol, experiments)}${toolsCatalog}
+${getSharedToolUseSection(experiments)}${toolsCatalog}
-${getToolUseGuidelinesSection(effectiveProtocol, experiments)}
+ ${getToolUseGuidelinesSection(experiments)}
${mcpServersSection}
diff --git a/src/core/prompts/tools/__tests__/access-mcp-resource.spec.ts b/src/core/prompts/tools/__tests__/access-mcp-resource.spec.ts
deleted file mode 100644
index 1a927937ff..0000000000
--- a/src/core/prompts/tools/__tests__/access-mcp-resource.spec.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-import { getAccessMcpResourceDescription } from "../access-mcp-resource"
-import { ToolArgs } from "../types"
-import { McpHub } from "../../../../services/mcp/McpHub"
-
-describe("getAccessMcpResourceDescription", () => {
- const baseArgs: Omit = {
- cwd: "/test",
- supportsComputerUse: false,
- }
-
- it("should return undefined when mcpHub is not provided", () => {
- const args: ToolArgs = {
- ...baseArgs,
- mcpHub: undefined,
- }
-
- const result = getAccessMcpResourceDescription(args)
- expect(result).toBeUndefined()
- })
-
- it("should return undefined when mcpHub has no servers with resources", () => {
- const mockMcpHub = {
- getServers: () => [
- {
- name: "test-server",
- resources: [],
- },
- ],
- } as unknown as McpHub
-
- const args: ToolArgs = {
- ...baseArgs,
- mcpHub: mockMcpHub,
- }
-
- const result = getAccessMcpResourceDescription(args)
- expect(result).toBeUndefined()
- })
-
- it("should return undefined when mcpHub has servers with undefined resources", () => {
- const mockMcpHub = {
- getServers: () => [
- {
- name: "test-server",
- resources: undefined,
- },
- ],
- } as unknown as McpHub
-
- const args: ToolArgs = {
- ...baseArgs,
- mcpHub: mockMcpHub,
- }
-
- const result = getAccessMcpResourceDescription(args)
- expect(result).toBeUndefined()
- })
-
- it("should return undefined when mcpHub has no servers", () => {
- const mockMcpHub = {
- getServers: () => [],
- } as unknown as McpHub
-
- const args: ToolArgs = {
- ...baseArgs,
- mcpHub: mockMcpHub,
- }
-
- const result = getAccessMcpResourceDescription(args)
- expect(result).toBeUndefined()
- })
-
- it("should return description when mcpHub has servers with resources", () => {
- const mockMcpHub = {
- getServers: () => [
- {
- name: "test-server",
- resources: [{ uri: "test://resource", name: "Test Resource" }],
- },
- ],
- } as unknown as McpHub
-
- const args: ToolArgs = {
- ...baseArgs,
- mcpHub: mockMcpHub,
- }
-
- const result = getAccessMcpResourceDescription(args)
- expect(result).toBeDefined()
- expect(result).toContain("## access_mcp_resource")
- expect(result).toContain("server_name")
- expect(result).toContain("uri")
- })
-
- it("should return description when at least one server has resources", () => {
- const mockMcpHub = {
- getServers: () => [
- {
- name: "server-without-resources",
- resources: [],
- },
- {
- name: "server-with-resources",
- resources: [{ uri: "test://resource", name: "Test Resource" }],
- },
- ],
- } as unknown as McpHub
-
- const args: ToolArgs = {
- ...baseArgs,
- mcpHub: mockMcpHub,
- }
-
- const result = getAccessMcpResourceDescription(args)
- expect(result).toBeDefined()
- expect(result).toContain("## access_mcp_resource")
- })
-})
diff --git a/src/core/prompts/tools/__tests__/attempt-completion.spec.ts b/src/core/prompts/tools/__tests__/attempt-completion.spec.ts
deleted file mode 100644
index 026d73789f..0000000000
--- a/src/core/prompts/tools/__tests__/attempt-completion.spec.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import { getAttemptCompletionDescription } from "../attempt-completion"
-
-describe("getAttemptCompletionDescription", () => {
- it("should NOT include command parameter in the description", () => {
- const args = {
- cwd: "/test/path",
- supportsComputerUse: false,
- }
-
- const description = getAttemptCompletionDescription(args)
-
- // Check that command parameter is NOT included (permanently disabled)
- expect(description).not.toContain("- command: (optional)")
- expect(description).not.toContain("A CLI command to execute to show a live demo")
- expect(description).not.toContain("Command to demonstrate result (optional)")
- expect(description).not.toContain("open index.html")
-
- // But should still have the basic structure
- expect(description).toContain("## attempt_completion")
- expect(description).toContain("- result: (required)")
- expect(description).toContain("")
- expect(description).toContain("")
- })
-
- it("should work when no args provided", () => {
- const description = getAttemptCompletionDescription()
-
- // Check that command parameter is NOT included (permanently disabled)
- expect(description).not.toContain("- command: (optional)")
- expect(description).not.toContain("A CLI command to execute to show a live demo")
- expect(description).not.toContain("Command to demonstrate result (optional)")
- expect(description).not.toContain("open index.html")
-
- // But should still have the basic structure
- expect(description).toContain("## attempt_completion")
- expect(description).toContain("- result: (required)")
- expect(description).toContain("")
- expect(description).toContain("")
- })
-
- it("should show example without command", () => {
- const args = {
- cwd: "/test/path",
- supportsComputerUse: false,
- }
-
- const description = getAttemptCompletionDescription(args)
-
- // Check example format
- expect(description).toContain("Example: Requesting to attempt completion with a result")
- expect(description).toContain("I've updated the CSS")
- expect(description).not.toContain("Example: Requesting to attempt completion with a result and command")
- })
-
- it("should contain core functionality description", () => {
- const description = getAttemptCompletionDescription()
-
- // Should contain core functionality
- const coreText = "After each tool use, the user will respond with the result of that tool use"
- expect(description).toContain(coreText)
-
- // Should contain the important note
- const importantNote = "IMPORTANT NOTE: This tool CANNOT be used until you've confirmed"
- expect(description).toContain(importantNote)
-
- // Should contain result parameter
- expect(description).toContain("- result: (required)")
- })
-})
diff --git a/src/core/prompts/tools/__tests__/fetch-instructions.spec.ts b/src/core/prompts/tools/__tests__/fetch-instructions.spec.ts
deleted file mode 100644
index ef01f132f5..0000000000
--- a/src/core/prompts/tools/__tests__/fetch-instructions.spec.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { getFetchInstructionsDescription } from "../fetch-instructions"
-
-describe("getFetchInstructionsDescription", () => {
- it("should include create_mcp_server when enableMcpServerCreation is true", () => {
- const description = getFetchInstructionsDescription(true)
-
- expect(description).toContain("create_mcp_server")
- expect(description).toContain("create_mode")
- expect(description).toContain("Example: Requesting instructions to create an MCP Server")
- expect(description).toContain("create_mcp_server")
- })
-
- it("should include create_mcp_server when enableMcpServerCreation is undefined (default behavior)", () => {
- const description = getFetchInstructionsDescription()
-
- expect(description).toContain("create_mcp_server")
- expect(description).toContain("create_mode")
- expect(description).toContain("Example: Requesting instructions to create an MCP Server")
- expect(description).toContain("create_mcp_server")
- })
-
- it("should exclude create_mcp_server when enableMcpServerCreation is false", () => {
- const description = getFetchInstructionsDescription(false)
-
- expect(description).not.toContain("create_mcp_server")
- expect(description).toContain("create_mode")
- expect(description).toContain("Example: Requesting instructions to create a Mode")
- expect(description).toContain("create_mode")
- expect(description).not.toContain("Example: Requesting instructions to create an MCP Server")
- })
-
- it("should have the correct structure", () => {
- const description = getFetchInstructionsDescription(true)
-
- expect(description).toContain("## fetch_instructions")
- expect(description).toContain("Description: Request to fetch instructions to perform a task")
- expect(description).toContain("Parameters:")
- expect(description).toContain("- task: (required) The task to get instructions for.")
- expect(description).toContain("")
- expect(description).toContain("")
- })
-
- it("should handle null value consistently (treat as default/undefined)", () => {
- const description = getFetchInstructionsDescription(null as any)
-
- // Should behave the same as undefined (default to true)
- expect(description).toContain("create_mcp_server")
- expect(description).toContain("create_mode")
- expect(description).toContain("Example: Requesting instructions to create an MCP Server")
- expect(description).toContain("create_mcp_server")
- })
-})
diff --git a/src/core/prompts/tools/__tests__/filter-tools-for-mode.spec.ts b/src/core/prompts/tools/__tests__/filter-tools-for-mode.spec.ts
deleted file mode 100644
index 5cdfe2f1e7..0000000000
--- a/src/core/prompts/tools/__tests__/filter-tools-for-mode.spec.ts
+++ /dev/null
@@ -1,912 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach } from "vitest"
-import type OpenAI from "openai"
-import type { ModeConfig, ModelInfo } from "@roo-code/types"
-import {
- filterNativeToolsForMode,
- filterMcpToolsForMode,
- applyModelToolCustomization,
- resolveToolAlias,
-} from "../filter-tools-for-mode"
-import * as toolsModule from "../../../../shared/tools"
-
-describe("filterNativeToolsForMode", () => {
- const mockNativeTools: OpenAI.Chat.ChatCompletionTool[] = [
- {
- type: "function",
- function: {
- name: "read_file",
- description: "Read files",
- parameters: {},
- },
- },
- {
- type: "function",
- function: {
- name: "write_to_file",
- description: "Write files",
- parameters: {},
- },
- },
- {
- type: "function",
- function: {
- name: "apply_diff",
- description: "Apply diff",
- parameters: {},
- },
- },
- {
- type: "function",
- function: {
- name: "execute_command",
- description: "Execute command",
- parameters: {},
- },
- },
- {
- type: "function",
- function: {
- name: "browser_action",
- description: "Browser action",
- parameters: {},
- },
- },
- {
- type: "function",
- function: {
- name: "ask_followup_question",
- description: "Ask question",
- parameters: {},
- },
- },
- {
- type: "function",
- function: {
- name: "attempt_completion",
- description: "Complete task",
- parameters: {},
- },
- },
- ]
-
- it("should filter tools for architect mode (read, browser, mcp only)", () => {
- const architectMode: ModeConfig = {
- slug: "architect",
- name: "Architect",
- roleDefinition: "Test",
- groups: ["read", "browser", "mcp"] as const,
- }
-
- const filtered = filterNativeToolsForMode(
- mockNativeTools,
- "architect",
- [architectMode],
- {},
- undefined,
- {},
- undefined,
- )
-
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
-
- // Should include read tools
- expect(toolNames).toContain("read_file")
-
- // Should NOT include edit tools
- expect(toolNames).not.toContain("write_to_file")
- expect(toolNames).not.toContain("apply_diff")
-
- // Should NOT include command tools
- expect(toolNames).not.toContain("execute_command")
-
- // Should include browser tools
- expect(toolNames).toContain("browser_action")
-
- // Should ALWAYS include always-available tools
- expect(toolNames).toContain("ask_followup_question")
- expect(toolNames).toContain("attempt_completion")
- })
-
- it("should filter tools for code mode (all groups)", () => {
- const codeMode: ModeConfig = {
- slug: "code",
- name: "Code",
- roleDefinition: "Test",
- groups: ["read", "edit", "browser", "command", "mcp"] as const,
- }
-
- const filtered = filterNativeToolsForMode(mockNativeTools, "code", [codeMode], {}, undefined, {}, undefined)
-
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
-
- // Should include all tools (code mode has all groups)
- expect(toolNames).toContain("read_file")
- expect(toolNames).toContain("write_to_file")
- expect(toolNames).toContain("apply_diff")
- expect(toolNames).toContain("execute_command")
- expect(toolNames).toContain("browser_action")
- expect(toolNames).toContain("ask_followup_question")
- expect(toolNames).toContain("attempt_completion")
- })
-
- it("should always include always-available tools regardless of mode groups", () => {
- const restrictiveMode: ModeConfig = {
- slug: "restrictive",
- name: "Restrictive",
- roleDefinition: "Test",
- groups: [] as const, // No groups
- }
-
- const filtered = filterNativeToolsForMode(
- mockNativeTools,
- "restrictive",
- [restrictiveMode],
- {},
- undefined,
- {},
- undefined,
- )
-
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
-
- // Should still include always-available tools
- expect(toolNames).toContain("ask_followup_question")
- expect(toolNames).toContain("attempt_completion")
-
- // Should NOT include any other tools
- expect(toolNames).not.toContain("read_file")
- expect(toolNames).not.toContain("write_to_file")
- expect(toolNames).not.toContain("execute_command")
- })
-
- it("should handle undefined mode by using default mode", () => {
- const filtered = filterNativeToolsForMode(mockNativeTools, undefined, undefined, {}, undefined, {}, undefined)
-
- // Should return some tools (default mode is code which has all groups)
- expect(filtered.length).toBeGreaterThan(0)
-
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
- expect(toolNames).toContain("ask_followup_question")
- expect(toolNames).toContain("attempt_completion")
- })
-
- it("should exclude codebase_search when codeIndexManager is not configured", () => {
- const codeMode: ModeConfig = {
- slug: "code",
- name: "Code",
- roleDefinition: "Test",
- groups: ["read", "edit", "browser", "command", "mcp"] as const,
- }
-
- const mockCodebaseSearchTool: OpenAI.Chat.ChatCompletionTool = {
- type: "function",
- function: {
- name: "codebase_search",
- description: "Search codebase",
- parameters: {},
- },
- }
-
- const toolsWithCodebaseSearch = [...mockNativeTools, mockCodebaseSearchTool]
-
- // Without codeIndexManager
- const filtered = filterNativeToolsForMode(
- toolsWithCodebaseSearch,
- "code",
- [codeMode],
- {},
- undefined,
- {},
- undefined,
- )
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
- expect(toolNames).not.toContain("codebase_search")
- })
-
- it("should exclude access_mcp_resource when mcpHub is not provided", () => {
- const codeMode: ModeConfig = {
- slug: "code",
- name: "Code",
- roleDefinition: "Test",
- groups: ["read", "edit", "browser", "command", "mcp"] as const,
- }
-
- const mockAccessMcpResourceTool: OpenAI.Chat.ChatCompletionTool = {
- type: "function",
- function: {
- name: "access_mcp_resource",
- description: "Access MCP resource",
- parameters: {},
- },
- }
-
- const toolsWithAccessMcpResource = [...mockNativeTools, mockAccessMcpResourceTool]
-
- // Without mcpHub
- const filtered = filterNativeToolsForMode(
- toolsWithAccessMcpResource,
- "code",
- [codeMode],
- {},
- undefined,
- {},
- undefined,
- )
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
- expect(toolNames).not.toContain("access_mcp_resource")
- })
-
- it("should exclude access_mcp_resource when mcpHub has no resources", () => {
- const codeMode: ModeConfig = {
- slug: "code",
- name: "Code",
- roleDefinition: "Test",
- groups: ["read", "edit", "browser", "command", "mcp"] as const,
- }
-
- const mockAccessMcpResourceTool: OpenAI.Chat.ChatCompletionTool = {
- type: "function",
- function: {
- name: "access_mcp_resource",
- description: "Access MCP resource",
- parameters: {},
- },
- }
-
- const toolsWithAccessMcpResource = [...mockNativeTools, mockAccessMcpResourceTool]
-
- // Mock mcpHub with no resources
- const mockMcpHub = {
- getServers: () => [
- {
- name: "test-server",
- resources: [],
- },
- ],
- } as any
-
- const filtered = filterNativeToolsForMode(
- toolsWithAccessMcpResource,
- "code",
- [codeMode],
- {},
- undefined,
- {},
- mockMcpHub,
- )
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
- expect(toolNames).not.toContain("access_mcp_resource")
- })
-
- it("should include access_mcp_resource when mcpHub has resources", () => {
- const codeMode: ModeConfig = {
- slug: "code",
- name: "Code",
- roleDefinition: "Test",
- groups: ["read", "edit", "browser", "command", "mcp"] as const,
- }
-
- const mockAccessMcpResourceTool: OpenAI.Chat.ChatCompletionTool = {
- type: "function",
- function: {
- name: "access_mcp_resource",
- description: "Access MCP resource",
- parameters: {},
- },
- }
-
- const toolsWithAccessMcpResource = [...mockNativeTools, mockAccessMcpResourceTool]
-
- // Mock mcpHub with resources
- const mockMcpHub = {
- getServers: () => [
- {
- name: "test-server",
- resources: [{ uri: "test://resource", name: "Test Resource" }],
- },
- ],
- } as any
-
- const filtered = filterNativeToolsForMode(
- toolsWithAccessMcpResource,
- "code",
- [codeMode],
- {},
- undefined,
- {},
- mockMcpHub,
- )
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
- expect(toolNames).toContain("access_mcp_resource")
- })
-
- it("should exclude update_todo_list when todoListEnabled is false", () => {
- const codeMode: ModeConfig = {
- slug: "code",
- name: "Code",
- roleDefinition: "Test",
- groups: ["read", "edit", "browser", "command", "mcp"] as const,
- }
-
- const mockTodoTool: OpenAI.Chat.ChatCompletionTool = {
- type: "function",
- function: {
- name: "update_todo_list",
- description: "Update todo list",
- parameters: {},
- },
- }
-
- const toolsWithTodo = [...mockNativeTools, mockTodoTool]
-
- const filtered = filterNativeToolsForMode(
- toolsWithTodo,
- "code",
- [codeMode],
- {},
- undefined,
- {
- todoListEnabled: false,
- },
- undefined,
- )
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
- expect(toolNames).not.toContain("update_todo_list")
- })
-
- it("should exclude generate_image when experiment is not enabled", () => {
- const codeMode: ModeConfig = {
- slug: "code",
- name: "Code",
- roleDefinition: "Test",
- groups: ["read", "edit", "browser", "command", "mcp"] as const,
- }
-
- const mockImageTool: OpenAI.Chat.ChatCompletionTool = {
- type: "function",
- function: {
- name: "generate_image",
- description: "Generate image",
- parameters: {},
- },
- }
-
- const toolsWithImage = [...mockNativeTools, mockImageTool]
-
- const filtered = filterNativeToolsForMode(
- toolsWithImage,
- "code",
- [codeMode],
- { imageGeneration: false },
- undefined,
- {},
- undefined,
- )
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
- expect(toolNames).not.toContain("generate_image")
- })
-
- it("should exclude run_slash_command when experiment is not enabled", () => {
- const codeMode: ModeConfig = {
- slug: "code",
- name: "Code",
- roleDefinition: "Test",
- groups: ["read", "edit", "browser", "command", "mcp"] as const,
- }
-
- const mockSlashCommandTool: OpenAI.Chat.ChatCompletionTool = {
- type: "function",
- function: {
- name: "run_slash_command",
- description: "Run slash command",
- parameters: {},
- },
- }
-
- const toolsWithSlashCommand = [...mockNativeTools, mockSlashCommandTool]
-
- const filtered = filterNativeToolsForMode(
- toolsWithSlashCommand,
- "code",
- [codeMode],
- { runSlashCommand: false },
- undefined,
- {},
- undefined,
- )
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
- expect(toolNames).not.toContain("run_slash_command")
- })
-})
-
-describe("filterMcpToolsForMode", () => {
- const mockMcpTools: OpenAI.Chat.ChatCompletionTool[] = [
- {
- type: "function",
- function: {
- name: "mcp_server1_tool1",
- description: "MCP tool 1",
- parameters: {},
- },
- },
- {
- type: "function",
- function: {
- name: "mcp_server1_tool2",
- description: "MCP tool 2",
- parameters: {},
- },
- },
- ]
-
- it("should include MCP tools when mode has mcp group", () => {
- const modeWithMcp: ModeConfig = {
- slug: "test-with-mcp",
- name: "Test",
- roleDefinition: "Test",
- groups: ["read", "mcp"] as const,
- }
-
- const filtered = filterMcpToolsForMode(mockMcpTools, "test-with-mcp", [modeWithMcp], {})
-
- expect(filtered).toHaveLength(2)
- expect(filtered).toEqual(mockMcpTools)
- })
-
- it("should exclude MCP tools when mode does not have mcp group", () => {
- const modeWithoutMcp: ModeConfig = {
- slug: "test-no-mcp",
- name: "Test",
- roleDefinition: "Test",
- groups: ["read", "edit"] as const,
- }
-
- const filtered = filterMcpToolsForMode(mockMcpTools, "test-no-mcp", [modeWithoutMcp], {})
-
- expect(filtered).toHaveLength(0)
- })
-
- it("should handle undefined mode by using default mode", () => {
- // Default mode (code) has mcp group
- const filtered = filterMcpToolsForMode(mockMcpTools, undefined, undefined, {})
-
- // Should include MCP tools since default mode has mcp group
- expect(filtered.length).toBeGreaterThan(0)
- })
-
- describe("applyModelToolCustomization", () => {
- const codeMode: ModeConfig = {
- slug: "code",
- name: "Code",
- roleDefinition: "Test",
- groups: ["read", "edit", "browser", "command", "mcp"] as const,
- }
-
- const architectMode: ModeConfig = {
- slug: "architect",
- name: "Architect",
- roleDefinition: "Test",
- groups: ["read", "browser", "mcp"] as const,
- }
-
- it("should return original tools when modelInfo is undefined", () => {
- const tools = new Set(["read_file", "write_to_file", "apply_diff"])
- const result = applyModelToolCustomization(tools, codeMode, undefined)
- expect(result.allowedTools).toEqual(tools)
- })
-
- it("should exclude tools specified in excludedTools", () => {
- const tools = new Set(["read_file", "write_to_file", "apply_diff"])
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- excludedTools: ["apply_diff"],
- }
- const result = applyModelToolCustomization(tools, codeMode, modelInfo)
- expect(result.allowedTools.has("read_file")).toBe(true)
- expect(result.allowedTools.has("write_to_file")).toBe(true)
- expect(result.allowedTools.has("apply_diff")).toBe(false)
- })
-
- it("should exclude multiple tools", () => {
- const tools = new Set(["read_file", "write_to_file", "apply_diff", "execute_command"])
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- excludedTools: ["apply_diff", "write_to_file"],
- }
- const result = applyModelToolCustomization(tools, codeMode, modelInfo)
- expect(result.allowedTools.has("read_file")).toBe(true)
- expect(result.allowedTools.has("execute_command")).toBe(true)
- expect(result.allowedTools.has("write_to_file")).toBe(false)
- expect(result.allowedTools.has("apply_diff")).toBe(false)
- })
-
- it("should include tools only if they belong to allowed groups", () => {
- const tools = new Set(["read_file"])
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- includedTools: ["write_to_file", "apply_diff"], // Both in edit group
- }
- const result = applyModelToolCustomization(tools, codeMode, modelInfo)
- expect(result.allowedTools.has("read_file")).toBe(true)
- expect(result.allowedTools.has("write_to_file")).toBe(true)
- expect(result.allowedTools.has("apply_diff")).toBe(true)
- })
-
- it("should NOT include tools from groups not allowed by mode", () => {
- const tools = new Set(["read_file"])
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- includedTools: ["write_to_file", "apply_diff"], // Edit group tools
- }
- // Architect mode doesn't have edit group
- const result = applyModelToolCustomization(tools, architectMode, modelInfo)
- expect(result.allowedTools.has("read_file")).toBe(true)
- expect(result.allowedTools.has("write_to_file")).toBe(false) // Not in allowed groups
- expect(result.allowedTools.has("apply_diff")).toBe(false) // Not in allowed groups
- })
-
- it("should apply both exclude and include operations", () => {
- const tools = new Set(["read_file", "write_to_file", "apply_diff"])
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- excludedTools: ["apply_diff"],
- includedTools: ["search_and_replace"], // Another edit tool (customTool)
- }
- const result = applyModelToolCustomization(tools, codeMode, modelInfo)
- expect(result.allowedTools.has("read_file")).toBe(true)
- expect(result.allowedTools.has("write_to_file")).toBe(true)
- expect(result.allowedTools.has("apply_diff")).toBe(false) // Excluded
- expect(result.allowedTools.has("search_and_replace")).toBe(true) // Included
- })
-
- it("should handle empty excludedTools and includedTools arrays", () => {
- const tools = new Set(["read_file", "write_to_file"])
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- excludedTools: [],
- includedTools: [],
- }
- const result = applyModelToolCustomization(tools, codeMode, modelInfo)
- expect(result.allowedTools).toEqual(tools)
- })
-
- it("should ignore excluded tools that are not in the original set", () => {
- const tools = new Set(["read_file", "write_to_file"])
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- excludedTools: ["apply_diff", "nonexistent_tool"],
- }
- const result = applyModelToolCustomization(tools, codeMode, modelInfo)
- expect(result.allowedTools.has("read_file")).toBe(true)
- expect(result.allowedTools.has("write_to_file")).toBe(true)
- expect(result.allowedTools.size).toBe(2)
- })
-
- it("should NOT include customTools by default", () => {
- const tools = new Set(["read_file", "write_to_file"])
- // Assume 'edit' group has a customTool defined in TOOL_GROUPS
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- // No includedTools specified
- }
- const result = applyModelToolCustomization(tools, codeMode, modelInfo)
- // customTools should not be in the result unless explicitly included
- expect(result.allowedTools.has("read_file")).toBe(true)
- expect(result.allowedTools.has("write_to_file")).toBe(true)
- })
-
- it("should NOT include tools that are not in any TOOL_GROUPS", () => {
- const tools = new Set(["read_file"])
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- includedTools: ["my_custom_tool"], // Not in any tool group
- }
- const result = applyModelToolCustomization(tools, codeMode, modelInfo)
- expect(result.allowedTools.has("read_file")).toBe(true)
- expect(result.allowedTools.has("my_custom_tool")).toBe(false)
- })
-
- it("should NOT include undefined tools even with allowed groups", () => {
- const tools = new Set(["read_file"])
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- includedTools: ["custom_edit_tool"], // Not in any tool group
- }
- // Even though architect mode has read group, undefined tools are not added
- const result = applyModelToolCustomization(tools, architectMode, modelInfo)
- expect(result.allowedTools.has("read_file")).toBe(true)
- expect(result.allowedTools.has("custom_edit_tool")).toBe(false)
- })
-
- describe("with customTools defined in TOOL_GROUPS", () => {
- const originalToolGroups = { ...toolsModule.TOOL_GROUPS }
-
- beforeEach(() => {
- // Add a customTool to the edit group
- ;(toolsModule.TOOL_GROUPS as any).edit = {
- ...originalToolGroups.edit,
- customTools: ["special_edit_tool"],
- }
- })
-
- afterEach(() => {
- // Restore original TOOL_GROUPS
- ;(toolsModule.TOOL_GROUPS as any).edit = originalToolGroups.edit
- })
-
- it("should include customTools when explicitly specified in includedTools", () => {
- const tools = new Set(["read_file", "write_to_file"])
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- includedTools: ["special_edit_tool"], // customTool from edit group
- }
- const result = applyModelToolCustomization(tools, codeMode, modelInfo)
- expect(result.allowedTools.has("read_file")).toBe(true)
- expect(result.allowedTools.has("write_to_file")).toBe(true)
- expect(result.allowedTools.has("special_edit_tool")).toBe(true) // customTool should be included
- })
-
- it("should NOT include customTools when not specified in includedTools", () => {
- const tools = new Set(["read_file", "write_to_file"])
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- // No includedTools specified
- }
- const result = applyModelToolCustomization(tools, codeMode, modelInfo)
- expect(result.allowedTools.has("read_file")).toBe(true)
- expect(result.allowedTools.has("write_to_file")).toBe(true)
- expect(result.allowedTools.has("special_edit_tool")).toBe(false) // customTool should NOT be included by default
- })
-
- it("should NOT include customTools from groups not allowed by mode", () => {
- const tools = new Set(["read_file"])
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- includedTools: ["special_edit_tool"], // customTool from edit group
- }
- // Architect mode doesn't have edit group
- const result = applyModelToolCustomization(tools, architectMode, modelInfo)
- expect(result.allowedTools.has("read_file")).toBe(true)
- expect(result.allowedTools.has("special_edit_tool")).toBe(false) // customTool should NOT be included
- })
- })
- })
-
- describe("filterNativeToolsForMode with model customization", () => {
- const mockNativeTools: OpenAI.Chat.ChatCompletionTool[] = [
- {
- type: "function",
- function: {
- name: "read_file",
- description: "Read files",
- parameters: {},
- },
- },
- {
- type: "function",
- function: {
- name: "write_to_file",
- description: "Write files",
- parameters: {},
- },
- },
- {
- type: "function",
- function: {
- name: "apply_diff",
- description: "Apply diff",
- parameters: {},
- },
- },
- {
- type: "function",
- function: {
- name: "execute_command",
- description: "Execute command",
- parameters: {},
- },
- },
- {
- type: "function",
- function: {
- name: "search_and_replace",
- description: "Search and replace",
- parameters: {},
- },
- },
- {
- type: "function",
- function: {
- name: "edit_file",
- description: "Edit file",
- parameters: {},
- },
- },
- ]
-
- it("should exclude tools when model specifies excludedTools", () => {
- const codeMode: ModeConfig = {
- slug: "code",
- name: "Code",
- roleDefinition: "Test",
- groups: ["read", "edit", "browser", "command", "mcp"] as const,
- }
-
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- excludedTools: ["apply_diff"],
- }
-
- const filtered = filterNativeToolsForMode(mockNativeTools, "code", [codeMode], {}, undefined, {
- modelInfo,
- })
-
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
-
- expect(toolNames).toContain("read_file")
- expect(toolNames).toContain("write_to_file")
- expect(toolNames).not.toContain("apply_diff") // Excluded by model
- })
-
- it("should include tools when model specifies includedTools from allowed groups", () => {
- const modeWithOnlyRead: ModeConfig = {
- slug: "limited",
- name: "Limited",
- roleDefinition: "Test",
- groups: ["read", "edit"] as const,
- }
-
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- includedTools: ["search_and_replace"], // Edit group customTool
- }
-
- const filtered = filterNativeToolsForMode(mockNativeTools, "limited", [modeWithOnlyRead], {}, undefined, {
- modelInfo,
- })
-
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
-
- expect(toolNames).toContain("search_and_replace") // Included by model
- })
-
- it("should NOT include tools from groups not allowed by mode", () => {
- const architectMode: ModeConfig = {
- slug: "architect",
- name: "Architect",
- roleDefinition: "Test",
- groups: ["read", "browser"] as const, // No edit group
- }
-
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- includedTools: ["write_to_file", "apply_diff"], // Edit group tools
- }
-
- const filtered = filterNativeToolsForMode(mockNativeTools, "architect", [architectMode], {}, undefined, {
- modelInfo,
- })
-
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
-
- expect(toolNames).toContain("read_file")
- expect(toolNames).not.toContain("write_to_file") // Not in mode's allowed groups
- expect(toolNames).not.toContain("apply_diff") // Not in mode's allowed groups
- })
-
- it("should combine excludedTools and includedTools", () => {
- const codeMode: ModeConfig = {
- slug: "code",
- name: "Code",
- roleDefinition: "Test",
- groups: ["read", "edit", "browser", "command", "mcp"] as const,
- }
-
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- excludedTools: ["apply_diff"],
- includedTools: ["search_and_replace"],
- }
-
- const filtered = filterNativeToolsForMode(mockNativeTools, "code", [codeMode], {}, undefined, {
- modelInfo,
- })
-
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
-
- expect(toolNames).toContain("write_to_file")
- expect(toolNames).toContain("search_and_replace") // Included
- expect(toolNames).not.toContain("apply_diff") // Excluded
- })
-
- it("should honor included aliases while respecting exclusions", () => {
- const codeMode: ModeConfig = {
- slug: "code",
- name: "Code",
- roleDefinition: "Test",
- groups: ["read", "edit", "browser", "command", "mcp"] as const,
- }
-
- const modelInfo: ModelInfo = {
- contextWindow: 100000,
- supportsPromptCache: false,
- excludedTools: ["apply_diff"],
- includedTools: ["edit_file", "write_file"],
- }
-
- const filtered = filterNativeToolsForMode(mockNativeTools, "code", [codeMode], {}, undefined, {
- modelInfo,
- })
-
- const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
-
- expect(toolNames).toContain("edit_file")
- expect(toolNames).toContain("write_file")
- expect(toolNames).not.toContain("apply_diff")
- expect(toolNames).not.toContain("write_to_file")
- })
- })
-})
-
-describe("resolveToolAlias", () => {
- it("should resolve known alias to canonical name", () => {
- // write_file is an alias for write_to_file (defined in TOOL_ALIASES)
- expect(resolveToolAlias("write_file")).toBe("write_to_file")
- })
-
- it("should return canonical name unchanged", () => {
- expect(resolveToolAlias("write_to_file")).toBe("write_to_file")
- expect(resolveToolAlias("read_file")).toBe("read_file")
- expect(resolveToolAlias("apply_diff")).toBe("apply_diff")
- })
-
- it("should return unknown tool names unchanged", () => {
- expect(resolveToolAlias("unknown_tool")).toBe("unknown_tool")
- expect(resolveToolAlias("custom_tool_xyz")).toBe("custom_tool_xyz")
- })
-
- it("should ensure allowedFunctionNames are consistent with functionDeclarations", () => {
- // This test documents the fix for the Gemini allowedFunctionNames issue.
- // When tools are renamed via aliasRenames, the alias names must be resolved
- // back to canonical names for allowedFunctionNames to match functionDeclarations.
- //
- // Example scenario:
- // - Model specifies includedTools: ["write_file"] (an alias)
- // - filterNativeToolsForMode returns tool with name "write_file"
- // - But allTools (functionDeclarations) contains "write_to_file" (canonical)
- // - If allowedFunctionNames contains "write_file", Gemini will error
- // - Resolving aliases ensures consistency: resolveToolAlias("write_file") -> "write_to_file"
-
- const aliasToolName = "write_file"
- const canonicalToolName = "write_to_file"
-
- // Simulate extracting name from a filtered tool that was renamed to alias
- const extractedName = aliasToolName
-
- // Before the fix: allowedFunctionNames would contain alias name
- // This would cause Gemini to error because "write_file" doesn't exist in functionDeclarations
-
- // After the fix: we resolve to canonical name
- const resolvedName = resolveToolAlias(extractedName)
-
- // The resolved name matches what's in functionDeclarations (canonical names)
- expect(resolvedName).toBe(canonicalToolName)
- })
-})
diff --git a/src/core/prompts/tools/__tests__/new-task.spec.ts b/src/core/prompts/tools/__tests__/new-task.spec.ts
deleted file mode 100644
index c110cffcd1..0000000000
--- a/src/core/prompts/tools/__tests__/new-task.spec.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-import { getNewTaskDescription } from "../new-task"
-import { ToolArgs } from "../types"
-
-describe("getNewTaskDescription", () => {
- it("should NOT show todos parameter at all when setting is disabled", () => {
- const args: ToolArgs = {
- cwd: "/test",
- supportsComputerUse: false,
- settings: {
- newTaskRequireTodos: false,
- },
- }
-
- const description = getNewTaskDescription(args)
-
- // Check that todos parameter is NOT shown at all
- expect(description).not.toContain("todos:")
- expect(description).not.toContain("todos parameter")
- expect(description).not.toContain("The initial todo list in markdown checklist format")
-
- // Should have a simple example without todos
- expect(description).toContain("Implement a new feature for the application")
-
- // Should NOT have any todos tags in examples
- expect(description).not.toContain("")
- expect(description).not.toContain("")
-
- // Should still have mode and message as required
- expect(description).toContain("mode: (required)")
- expect(description).toContain("message: (required)")
- })
-
- it("should show todos as required when setting is enabled", () => {
- const args: ToolArgs = {
- cwd: "/test",
- supportsComputerUse: false,
- settings: {
- newTaskRequireTodos: true,
- },
- }
-
- const description = getNewTaskDescription(args)
-
- // Check that todos is marked as required
- expect(description).toContain("todos: (required)")
- expect(description).toContain("and initial todo list")
- expect(description).toContain("The initial todo list in markdown checklist format")
-
- // Should not contain any mention of optional for todos
- expect(description).not.toContain("todos: (optional)")
- expect(description).not.toContain("optional initial todo list")
-
- // Should include todos in the example
- expect(description).toContain("")
- expect(description).toContain("")
- expect(description).toContain("Set up auth middleware")
- })
-
- it("should NOT show todos parameter when settings is undefined", () => {
- const args: ToolArgs = {
- cwd: "/test",
- supportsComputerUse: false,
- settings: undefined,
- }
-
- const description = getNewTaskDescription(args)
-
- // Check that todos parameter is NOT shown by default
- expect(description).not.toContain("todos:")
- expect(description).not.toContain("The initial todo list in markdown checklist format")
- expect(description).not.toContain("")
- expect(description).not.toContain("")
- })
-
- it("should NOT show todos parameter when newTaskRequireTodos is undefined", () => {
- const args: ToolArgs = {
- cwd: "/test",
- supportsComputerUse: false,
- settings: {},
- }
-
- const description = getNewTaskDescription(args)
-
- // Check that todos parameter is NOT shown by default
- expect(description).not.toContain("todos:")
- expect(description).not.toContain("The initial todo list in markdown checklist format")
- expect(description).not.toContain("")
- expect(description).not.toContain("")
- })
-
- it("should include todos in examples only when setting is enabled", () => {
- const argsWithSettingOff: ToolArgs = {
- cwd: "/test",
- supportsComputerUse: false,
- settings: {
- newTaskRequireTodos: false,
- },
- }
-
- const argsWithSettingOn: ToolArgs = {
- cwd: "/test",
- supportsComputerUse: false,
- settings: {
- newTaskRequireTodos: true,
- },
- }
-
- const descriptionOff = getNewTaskDescription(argsWithSettingOff)
- const descriptionOn = getNewTaskDescription(argsWithSettingOn)
-
- // When setting is on, should include todos in main example
- expect(descriptionOn).toContain("Implement user authentication")
- expect(descriptionOn).toContain("[ ] Set up auth middleware")
- expect(descriptionOn).toContain("")
- expect(descriptionOn).toContain("")
-
- // When setting is off, should NOT include any todos references
- expect(descriptionOff).not.toContain("")
- expect(descriptionOff).not.toContain("")
- expect(descriptionOff).not.toContain("[ ] Set up auth middleware")
- expect(descriptionOff).not.toContain("[ ] First task to complete")
-
- // When setting is off, main example should be simple
- const usagePattern = /\s*.*<\/mode>\s*.*<\/message>\s*<\/new_task>/s
- expect(descriptionOff).toMatch(usagePattern)
- })
-})
diff --git a/src/core/prompts/tools/access-mcp-resource.ts b/src/core/prompts/tools/access-mcp-resource.ts
deleted file mode 100644
index 3807aab6bd..0000000000
--- a/src/core/prompts/tools/access-mcp-resource.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { ToolArgs } from "./types"
-import { McpHub } from "../../../services/mcp/McpHub"
-
-/**
- * Helper function to check if any MCP server has resources available
- */
-function hasAnyMcpResources(mcpHub: McpHub): boolean {
- const servers = mcpHub.getServers()
- return servers.some((server) => server.resources && server.resources.length > 0)
-}
-
-export function getAccessMcpResourceDescription(args: ToolArgs): string | undefined {
- if (!args.mcpHub || !hasAnyMcpResources(args.mcpHub)) {
- return undefined
- }
- return `## access_mcp_resource
-Description: Request to access a resource provided by a connected MCP server. Resources represent data sources that can be used as context, such as files, API responses, or system information.
-Parameters:
-- server_name: (required) The name of the MCP server providing the resource
-- uri: (required) The URI identifying the specific resource to access
-Usage:
-
-server name here
-resource URI here
-
-
-Example: Requesting to access an MCP resource
-
-
-weather-server
-weather://san-francisco/current
-`
-}
diff --git a/src/core/prompts/tools/ask-followup-question.ts b/src/core/prompts/tools/ask-followup-question.ts
deleted file mode 100644
index c40684b8bc..0000000000
--- a/src/core/prompts/tools/ask-followup-question.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-export function getAskFollowupQuestionDescription(): string {
- return `## ask_followup_question
-Description: Ask the user a question to gather additional information needed to complete the task. Use when you need clarification or more details to proceed effectively.
-
-Parameters:
-- question: (required) A clear, specific question addressing the information needed
-- follow_up: (required) A list of 2-4 suggested answers, each in its own tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
-
-Usage:
-
-Your question here
-
-First suggestion
-Action with mode switch
-
-
-
-Example:
-
-What is the path to the frontend-config.json file?
-
-./src/frontend-config.json
-./config/frontend-config.json
-./frontend-config.json
-
-`
-}
diff --git a/src/core/prompts/tools/attempt-completion.ts b/src/core/prompts/tools/attempt-completion.ts
deleted file mode 100644
index 62f0827f98..0000000000
--- a/src/core/prompts/tools/attempt-completion.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { ToolArgs } from "./types"
-
-export function getAttemptCompletionDescription(args?: ToolArgs): string {
- return `## attempt_completion
-Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
-IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.
-Parameters:
-- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.
-Usage:
-
-
-Your final result description here
-
-
-
-Example: Requesting to attempt completion with a result
-
-
-I've updated the CSS
-
-`
-}
diff --git a/src/core/prompts/tools/browser-action.ts b/src/core/prompts/tools/browser-action.ts
deleted file mode 100644
index 88c7343d0a..0000000000
--- a/src/core/prompts/tools/browser-action.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-import { ToolArgs } from "./types"
-
-export function getBrowserActionDescription(args: ToolArgs): string | undefined {
- if (!args.supportsComputerUse) {
- return undefined
- }
- return `## browser_action
-Description: Request to interact with a Puppeteer-controlled browser. Every action, except \`close\`, will be responded to with a screenshot of the browser's current state, along with any new console logs. You may only perform one browser action per message, and wait for the user's response including a screenshot and logs to determine the next action.
-
-This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. Use it at key stages of web development tasks - such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. Analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues.
-
-The user may ask generic non-development tasks (such as "what's the latest news" or "look up the weather"), in which case you might use this tool to complete the task if it makes sense to do so, rather than trying to create a website or using curl to answer the question. However, if an available MCP server tool or resource can be used instead, you should prefer to use it over browser_action.
-
-**Browser Session Lifecycle:**
-- Browser sessions **start** with \`launch\` and **end** with \`close\`
-- The session remains active across multiple messages and tool uses
-- You can use other tools while the browser session is active - it will stay open in the background
-
-Parameters:
-- action: (required) The action to perform. The available actions are:
- * launch: Launch a new Puppeteer-controlled browser instance at the specified URL. This **must always be the first action**.
- - Use with the \`url\` parameter to provide the URL.
- - Ensure the URL is valid and includes the appropriate protocol (e.g. http://localhost:3000/page, file:///path/to/file.html, etc.)
- * hover: Move the cursor to a specific x,y coordinate.
- - Use with the \`coordinate\` parameter to specify the location.
- - Always move to the center of an element (icon, button, link, etc.) based on coordinates derived from a screenshot.
- * click: Click at a specific x,y coordinate.
- - Use with the \`coordinate\` parameter to specify the location.
- - Always click in the center of an element (icon, button, link, etc.) based on coordinates derived from a screenshot.
- * type: Type a string of text on the keyboard. You might use this after clicking on a text field to input text.
- - Use with the \`text\` parameter to provide the string to type.
- * press: Press a single keyboard key or key combination (e.g., Enter, Tab, Escape, Cmd+K, Shift+Enter).
- - Use with the \`text\` parameter to provide the key name or combination.
- - For single keys: Enter, Tab, Escape, etc.
- - For key combinations: Cmd+K, Ctrl+C, Shift+Enter, Alt+F4, etc.
- - Supported modifiers: Cmd/Command/Meta, Ctrl/Control, Shift, Alt/Option
- - Example: Cmd+K or Shift+Enter
- * resize: Resize the viewport to a specific w,h size.
- - Use with the \`size\` parameter to specify the new size.
- * scroll_down: Scroll down the page by one page height.
- * scroll_up: Scroll up the page by one page height.
- * screenshot: Take a screenshot and save it to a file.
- - Use with the \`path\` parameter to specify the destination file path.
- - Supported formats: .png, .jpeg, .webp
- - Example: \`screenshot\` with \`screenshots/result.png\`
- * close: Close the Puppeteer-controlled browser instance. This **must always be the final browser action**.
- - Example: \`close\`
-- url: (optional) Use this for providing the URL for the \`launch\` action.
- * Example: https://example.com
-- coordinate: (optional) The X and Y coordinates for the \`click\` and \`hover\` actions.
- * **CRITICAL**: Screenshot dimensions are NOT the same as the browser viewport dimensions
- * Format: x,y@widthxheight
- * Measure x,y on the screenshot image you see in chat
- * The widthxheight MUST be the EXACT pixel size of that screenshot image (never the browser viewport)
- * Never use the browser viewport size for widthxheight - the viewport is only a reference and is often larger than the screenshot
- * Images are often downscaled before you see them, so the screenshot's dimensions will likely be smaller than the viewport
- * Example A: If the screenshot you see is 1094x1092 and you want to click (450,300) on that image, use: 450,300@1094x1092
- * Example B: If the browser viewport is 1280x800 but the screenshot is 1000x625 and you want to click (500,300) on the screenshot, use: 500,300@1000x625
-- size: (optional) The width and height for the \`resize\` action.
- * Example: 1280,720
-- text: (optional) Use this for providing the text for the \`type\` action.
- * Example: Hello, world!
-- path: (optional) File path for the \`screenshot\` action. Path is relative to the workspace.
- * Supported formats: .png, .jpeg, .webp
- * Example: screenshots/my-screenshot.png
-Usage:
-
-Action to perform (e.g., launch, click, type, press, scroll_down, scroll_up, close)
-URL to launch the browser at (optional)
-x,y@widthxheight coordinates (optional)
-Text to type (optional)
-
-
-Example: Requesting to launch a browser at https://example.com
-
-launch
-https://example.com
-
-
-Example: Requesting to click on the element at coordinates 450,300 on a 1024x768 image
-
-click
-450,300@1024x768
-
-
-Example: Taking a screenshot and saving it to a file
-
-screenshot
-screenshots/result.png
-`
-}
diff --git a/src/core/prompts/tools/codebase-search.ts b/src/core/prompts/tools/codebase-search.ts
deleted file mode 100644
index f613039215..0000000000
--- a/src/core/prompts/tools/codebase-search.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { ToolArgs } from "./types"
-
-export function getCodebaseSearchDescription(args: ToolArgs): string {
- return `## codebase_search
-Description: Find files most relevant to the search query using semantic search. Searches based on meaning rather than exact text matches. By default searches entire workspace. Reuse the user's exact wording unless there's a clear reason not to - their phrasing often helps semantic search. Queries MUST be in English (translate if needed).
-
-**CRITICAL: For ANY exploration of code you haven't examined yet in this conversation, you MUST use this tool FIRST before any other search or file exploration tools.** This applies throughout the entire conversation, not just at the beginning. This tool uses semantic search to find relevant code based on meaning rather than just keywords, making it far more effective than regex-based search_files for understanding implementations. Even if you've already explored some code, any new area of exploration requires codebase_search first.
-
-Parameters:
-- query: (required) The search query. Reuse the user's exact wording/question format unless there's a clear reason not to.
-- path: (optional) Limit search to specific subdirectory (relative to the current workspace directory ${args.cwd}). Leave empty for entire workspace.
-
-Usage:
-
-Your natural language query here
-Optional subdirectory path
-
-
-Example: Searching for user authentication code
-
-User login and password hashing
-src/auth
-
-
-Example: Searching entire workspace
-
-database connection pooling
-
-
-`
-}
diff --git a/src/core/prompts/tools/execute-command.ts b/src/core/prompts/tools/execute-command.ts
deleted file mode 100644
index c1fc1ea3f1..0000000000
--- a/src/core/prompts/tools/execute-command.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ToolArgs } from "./types"
-
-export function getExecuteCommandDescription(args: ToolArgs): string | undefined {
- return `## execute_command
-Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Prefer relative commands and paths that avoid location sensitivity for terminal consistency, e.g: \`touch ./testdata/example.file\`, \`dir ./examples/model1/data/yaml\`, or \`go test ./cmd/front --config ./cmd/front/config.yml\`. If directed by the user, you may open a terminal in a different directory by using the \`cwd\` parameter.
-Parameters:
-- command: (required) The CLI command to execute. This should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions.
-- cwd: (optional) The working directory to execute the command in (default: ${args.cwd})
-Usage:
-
-Your command here
-Working directory path (optional)
-
-
-Example: Requesting to execute npm run dev
-
-npm run dev
-
-
-Example: Requesting to execute ls in a specific directory if directed
-
-ls -la
-/home/user/projects
-`
-}
diff --git a/src/core/prompts/tools/fetch-instructions.ts b/src/core/prompts/tools/fetch-instructions.ts
deleted file mode 100644
index dd9cbb80da..0000000000
--- a/src/core/prompts/tools/fetch-instructions.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Generates the fetch_instructions tool description.
- * @param enableMcpServerCreation - Whether to include MCP server creation task.
- * Defaults to true when undefined.
- */
-export function getFetchInstructionsDescription(enableMcpServerCreation?: boolean): string {
- const tasks =
- enableMcpServerCreation !== false
- ? ` create_mcp_server
- create_mode`
- : ` create_mode`
-
- const example =
- enableMcpServerCreation !== false
- ? `Example: Requesting instructions to create an MCP Server
-
-
-create_mcp_server
-`
- : `Example: Requesting instructions to create a Mode
-
-
-create_mode
-`
-
- return `## fetch_instructions
-Description: Request to fetch instructions to perform a task
-Parameters:
-- task: (required) The task to get instructions for. This can take the following values:
-${tasks}
-
-${example}`
-}
diff --git a/src/core/prompts/tools/filter-tools-for-mode.ts b/src/core/prompts/tools/filter-tools-for-mode.ts
index f296c1b5c5..79db5d6edc 100644
--- a/src/core/prompts/tools/filter-tools-for-mode.ts
+++ b/src/core/prompts/tools/filter-tools-for-mode.ts
@@ -211,7 +211,7 @@ export function applyModelToolCustomization(
/**
* Filters native tools based on mode restrictions and model customization.
- * This ensures native tools are filtered the same way XML tools are filtered in the system prompt.
+ * This ensures native tools are filtered consistently with mode/tool permissions.
*
* @param nativeTools - Array of all available native tools
* @param mode - Current mode slug
diff --git a/src/core/prompts/tools/generate-image.ts b/src/core/prompts/tools/generate-image.ts
deleted file mode 100644
index 458b7ae8cf..0000000000
--- a/src/core/prompts/tools/generate-image.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { ToolArgs } from "./types"
-
-export function getGenerateImageDescription(args: ToolArgs): string {
- return `## generate_image
-Description: Request to generate or edit an image using AI models through OpenRouter API. This tool can create new images from text prompts or modify existing images based on your instructions. When an input image is provided, the AI will apply the requested edits, transformations, or enhancements to that image.
-Parameters:
-- prompt: (required) The text prompt describing what to generate or how to edit the image
-- path: (required) The file path where the generated/edited image should be saved (relative to the current workspace directory ${args.cwd}). The tool will automatically add the appropriate image extension if not provided.
-- image: (optional) The file path to an input image to edit or transform (relative to the current workspace directory ${args.cwd}). Supported formats: PNG, JPG, JPEG, GIF, WEBP.
-Usage:
-
-Your image description here
-path/to/save/image.png
-path/to/input/image.jpg
-
-
-Example: Requesting to generate a sunset image
-
-A beautiful sunset over mountains with vibrant orange and purple colors
-images/sunset.png
-
-
-Example: Editing an existing image
-
-Transform this image into a watercolor painting style
-images/watercolor-output.png
-images/original-photo.jpg
-
-
-Example: Upscaling and enhancing an image
-
-Upscale this image to higher resolution, enhance details, improve clarity and sharpness while maintaining the original content and composition
-images/enhanced-photo.png
-images/low-res-photo.jpg
-`
-}
diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts
deleted file mode 100644
index b75725a99b..0000000000
--- a/src/core/prompts/tools/index.ts
+++ /dev/null
@@ -1,172 +0,0 @@
-import type { ToolName, ModeConfig } from "@roo-code/types"
-
-import { TOOL_GROUPS, ALWAYS_AVAILABLE_TOOLS, DiffStrategy } from "../../../shared/tools"
-import { Mode, getModeConfig, getGroupName } from "../../../shared/modes"
-
-import { isToolAllowedForMode } from "../../tools/validateToolUse"
-
-import { McpHub } from "../../../services/mcp/McpHub"
-import { CodeIndexManager } from "../../../services/code-index/manager"
-
-import { ToolArgs } from "./types"
-import { getExecuteCommandDescription } from "./execute-command"
-import { getReadFileDescription } from "./read-file"
-import { getFetchInstructionsDescription } from "./fetch-instructions"
-import { getWriteToFileDescription } from "./write-to-file"
-import { getSearchFilesDescription } from "./search-files"
-import { getListFilesDescription } from "./list-files"
-import { getBrowserActionDescription } from "./browser-action"
-import { getAskFollowupQuestionDescription } from "./ask-followup-question"
-import { getAttemptCompletionDescription } from "./attempt-completion"
-import { getUseMcpToolDescription } from "./use-mcp-tool"
-import { getAccessMcpResourceDescription } from "./access-mcp-resource"
-import { getSwitchModeDescription } from "./switch-mode"
-import { getNewTaskDescription } from "./new-task"
-import { getCodebaseSearchDescription } from "./codebase-search"
-import { getUpdateTodoListDescription } from "./update-todo-list"
-import { getRunSlashCommandDescription } from "./run-slash-command"
-import { getGenerateImageDescription } from "./generate-image"
-
-// Map of tool names to their description functions
-const toolDescriptionMap: Record string | undefined> = {
- execute_command: (args) => getExecuteCommandDescription(args),
- read_file: (args) => getReadFileDescription(args),
- fetch_instructions: (args) => getFetchInstructionsDescription(args.settings?.enableMcpServerCreation),
- write_to_file: (args) => getWriteToFileDescription(args),
- search_files: (args) => getSearchFilesDescription(args),
- list_files: (args) => getListFilesDescription(args),
- browser_action: (args) => getBrowserActionDescription(args),
- ask_followup_question: () => getAskFollowupQuestionDescription(),
- attempt_completion: (args) => getAttemptCompletionDescription(args),
- use_mcp_tool: (args) => getUseMcpToolDescription(args),
- access_mcp_resource: (args) => getAccessMcpResourceDescription(args),
- codebase_search: (args) => getCodebaseSearchDescription(args),
- switch_mode: () => getSwitchModeDescription(),
- new_task: (args) => getNewTaskDescription(args),
- apply_diff: (args) =>
- args.diffStrategy ? args.diffStrategy.getToolDescription({ cwd: args.cwd, toolOptions: args.toolOptions }) : "",
- update_todo_list: (args) => getUpdateTodoListDescription(args),
- run_slash_command: () => getRunSlashCommandDescription(),
- generate_image: (args) => getGenerateImageDescription(args),
-}
-
-export function getToolDescriptionsForMode(
- mode: Mode,
- cwd: string,
- supportsComputerUse: boolean,
- codeIndexManager?: CodeIndexManager,
- diffStrategy?: DiffStrategy,
- browserViewportSize?: string,
- mcpHub?: McpHub,
- customModes?: ModeConfig[],
- experiments?: Record,
- partialReadsEnabled?: boolean,
- settings?: Record,
- enableMcpServerCreation?: boolean,
- modelId?: string,
-): string {
- const config = getModeConfig(mode, customModes)
- const args: ToolArgs = {
- cwd,
- supportsComputerUse,
- diffStrategy,
- browserViewportSize,
- mcpHub,
- partialReadsEnabled,
- settings: {
- ...settings,
- enableMcpServerCreation,
- modelId,
- },
- experiments,
- }
-
- const tools = new Set()
-
- // Add tools from mode's groups
- config.groups.forEach((groupEntry) => {
- const groupName = getGroupName(groupEntry)
- const toolGroup = TOOL_GROUPS[groupName]
- if (toolGroup) {
- toolGroup.tools.forEach((tool) => {
- if (
- isToolAllowedForMode(
- tool as ToolName,
- mode,
- customModes ?? [],
- undefined,
- undefined,
- experiments ?? {},
- )
- ) {
- tools.add(tool)
- }
- })
- }
- })
-
- // Add always available tools
- ALWAYS_AVAILABLE_TOOLS.forEach((tool) => tools.add(tool))
-
- // Conditionally exclude codebase_search if feature is disabled or not configured
- if (
- !codeIndexManager ||
- !(codeIndexManager.isFeatureEnabled && codeIndexManager.isFeatureConfigured && codeIndexManager.isInitialized)
- ) {
- tools.delete("codebase_search")
- }
-
- // Conditionally exclude update_todo_list if disabled in settings
- if (settings?.todoListEnabled === false) {
- tools.delete("update_todo_list")
- }
-
- // Conditionally exclude generate_image if experiment is not enabled
- if (!experiments?.imageGeneration) {
- tools.delete("generate_image")
- }
-
- // Conditionally exclude run_slash_command if experiment is not enabled
- if (!experiments?.runSlashCommand) {
- tools.delete("run_slash_command")
- }
-
- // Map tool descriptions for allowed tools
- const descriptions = Array.from(tools).map((toolName) => {
- const descriptionFn = toolDescriptionMap[toolName]
- if (!descriptionFn) {
- return undefined
- }
-
- const description = descriptionFn({
- ...args,
- toolOptions: undefined, // No tool options in group-based approach
- })
-
- return description
- })
-
- return `# Tools\n\n${descriptions.filter(Boolean).join("\n\n")}`
-}
-
-// Export individual description functions for backward compatibility
-export {
- getExecuteCommandDescription,
- getReadFileDescription,
- getFetchInstructionsDescription,
- getWriteToFileDescription,
- getSearchFilesDescription,
- getListFilesDescription,
- getBrowserActionDescription,
- getAskFollowupQuestionDescription,
- getAttemptCompletionDescription,
- getUseMcpToolDescription,
- getAccessMcpResourceDescription,
- getSwitchModeDescription,
- getCodebaseSearchDescription,
- getRunSlashCommandDescription,
- getGenerateImageDescription,
-}
-
-// Export native tool definitions (JSON schema format for OpenAI-compatible APIs)
-export { nativeTools } from "./native-tools"
diff --git a/src/core/prompts/tools/list-files.ts b/src/core/prompts/tools/list-files.ts
deleted file mode 100644
index 96c43ea4a6..0000000000
--- a/src/core/prompts/tools/list-files.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { ToolArgs } from "./types"
-
-export function getListFilesDescription(args: ToolArgs): string {
- return `## list_files
-Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
-Parameters:
-- path: (required) The path of the directory to list contents for (relative to the current workspace directory ${args.cwd})
-- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
-Usage:
-
-Directory path here
-true or false (optional)
-
-
-Example: Requesting to list all files in the current directory
-
-.
-false
-`
-}
diff --git a/src/core/prompts/tools/new-task.ts b/src/core/prompts/tools/new-task.ts
deleted file mode 100644
index bba6c6250f..0000000000
--- a/src/core/prompts/tools/new-task.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-import { ToolArgs } from "./types"
-
-/**
- * Prompt when todos are NOT required (default)
- */
-const PROMPT_WITHOUT_TODOS = `## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-
-Example:
-
-code
-Implement a new feature for the application
-
-`
-
-/**
- * Prompt when todos ARE required
- */
-const PROMPT_WITH_TODOS = `## new_task
-Description: This will let you create a new task instance in the chosen mode using your provided message and initial todo list.
-
-Parameters:
-- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
-- message: (required) The initial user message or instructions for this new task.
-- todos: (required) The initial todo list in markdown checklist format for the new task.
-
-Usage:
-
-your-mode-slug-here
-Your initial instructions here
-
-[ ] First task to complete
-[ ] Second task to complete
-[ ] Third task to complete
-
-
-
-Example:
-
-code
-Implement user authentication
-
-[ ] Set up auth middleware
-[ ] Create login endpoint
-[ ] Add session management
-[ ] Write tests
-
-
-
-`
-
-export function getNewTaskDescription(args: ToolArgs): string {
- const todosRequired = args.settings?.newTaskRequireTodos === true
-
- // Simply return the appropriate prompt based on the setting
- return todosRequired ? PROMPT_WITH_TODOS : PROMPT_WITHOUT_TODOS
-}
diff --git a/src/core/prompts/tools/read-file.ts b/src/core/prompts/tools/read-file.ts
deleted file mode 100644
index 86f4dc8c64..0000000000
--- a/src/core/prompts/tools/read-file.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import { ToolArgs } from "./types"
-
-export function getReadFileDescription(args: ToolArgs): string {
- const maxConcurrentReads = args.settings?.maxConcurrentFileReads ?? 5
- const isMultipleReadsEnabled = maxConcurrentReads > 1
-
- return `## read_file
-Description: Request to read the contents of ${isMultipleReadsEnabled ? "one or more files" : "a file"}. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code.${args.partialReadsEnabled ? " Use line ranges to efficiently read specific portions of large files." : ""} Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
-
-${isMultipleReadsEnabled ? `**IMPORTANT: You can read a maximum of ${maxConcurrentReads} files in a single request.** If you need to read more files, use multiple sequential read_file requests.` : "**IMPORTANT: Multiple file reads are currently disabled. You can only read one file at a time.**"}
-
-${args.partialReadsEnabled ? `By specifying line ranges, you can efficiently read specific portions of large files without loading the entire file into memory.` : ""}
-Parameters:
-- args: Contains one or more file elements, where each file contains:
- - path: (required) File path (relative to workspace directory ${args.cwd})
- ${args.partialReadsEnabled ? `- line_range: (optional) One or more line range elements in format "start-end" (1-based, inclusive)` : ""}
-
-Usage:
-
-
-
- path/to/file
- ${args.partialReadsEnabled ? `start-end` : ""}
-
-
-
-
-Examples:
-
-1. Reading a single file:
-
-
-
- src/app.ts
- ${args.partialReadsEnabled ? `1-1000` : ""}
-
-
-
-
-${isMultipleReadsEnabled ? `2. Reading multiple files (within the ${maxConcurrentReads}-file limit):` : ""}${
- isMultipleReadsEnabled
- ? `
-
-
-
- src/app.ts
- ${
- args.partialReadsEnabled
- ? `1-50
- 100-150`
- : ""
- }
-
-
- src/utils.ts
- ${args.partialReadsEnabled ? `10-20` : ""}
-
-
-`
- : ""
- }
-
-${isMultipleReadsEnabled ? "3. " : "2. "}Reading an entire file:
-
-
-
- config.json
-
-
-
-
-IMPORTANT: You MUST use this Efficient Reading Strategy:
-- ${isMultipleReadsEnabled ? `You MUST read all related files and implementations together in a single operation (up to ${maxConcurrentReads} files at once)` : "You MUST read files one at a time, as multiple file reads are currently disabled"}
-- You MUST obtain all necessary context before proceeding with changes
-${
- args.partialReadsEnabled
- ? `- You MUST use line ranges to read specific portions of large files, rather than reading entire files when not needed
-- You MUST combine adjacent line ranges (<10 lines apart)
-- You MUST use multiple ranges for content separated by >10 lines
-- You MUST include sufficient line context for planned modifications while keeping ranges minimal
-`
- : ""
-}
-${isMultipleReadsEnabled ? `- When you need to read more than ${maxConcurrentReads} files, prioritize the most critical files first, then use subsequent read_file requests for additional files` : ""}`
-}
diff --git a/src/core/prompts/tools/run-slash-command.ts b/src/core/prompts/tools/run-slash-command.ts
deleted file mode 100644
index 27047dcbaa..0000000000
--- a/src/core/prompts/tools/run-slash-command.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Generates the run_slash_command tool description.
- */
-export function getRunSlashCommandDescription(): string {
- return `## run_slash_command
-Description: Execute a slash command to get specific instructions or content. Slash commands are predefined templates that provide detailed guidance for common tasks.
-
-Parameters:
-- command: (required) The name of the slash command to execute (e.g., "init", "test", "deploy")
-- args: (optional) Additional arguments or context to pass to the command
-
-Usage:
-
-command_name
-optional arguments
-
-
-Examples:
-
-1. Running the init command to analyze a codebase:
-
-init
-
-
-2. Running a command with additional context:
-
-test
-focus on integration tests
-
-
-The command content will be returned for you to execute or follow as instructions.`
-}
diff --git a/src/core/prompts/tools/search-files.ts b/src/core/prompts/tools/search-files.ts
deleted file mode 100644
index f0af9f8a23..0000000000
--- a/src/core/prompts/tools/search-files.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { ToolArgs } from "./types"
-
-export function getSearchFilesDescription(args: ToolArgs): string {
- return `## search_files
-Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
-
-Craft your regex patterns carefully to balance specificity and flexibility. Use this tool to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include surrounding context, so analyze the surrounding code to better understand the matches. Leverage this 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.
-
-Parameters:
-- path: (required) The path of the directory to search in (relative to the current workspace directory ${args.cwd}). This directory will be recursively searched.
-- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
-- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
-
-Usage:
-
-Directory path here
-Your regex pattern here
-file pattern here (optional)
-
-
-Example: Searching for all .ts files in the current directory
-
-.
-.*
-*.ts
-
-
-Example: Searching for function definitions in JavaScript files
-
-src
-function\\s+\\w+
-*.js
-`
-}
diff --git a/src/core/prompts/tools/switch-mode.ts b/src/core/prompts/tools/switch-mode.ts
deleted file mode 100644
index a8c64d1e10..0000000000
--- a/src/core/prompts/tools/switch-mode.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-export function getSwitchModeDescription(): string {
- return `## switch_mode
-Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch.
-Parameters:
-- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect")
-- reason: (optional) The reason for switching modes
-Usage:
-
-Mode slug here
-Reason for switching here
-
-
-Example: Requesting to switch to code mode
-
-code
-Need to make code changes
-`
-}
diff --git a/src/core/prompts/tools/types.ts b/src/core/prompts/tools/types.ts
deleted file mode 100644
index 9471d100d7..0000000000
--- a/src/core/prompts/tools/types.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { DiffStrategy } from "../../../shared/tools"
-import { McpHub } from "../../../services/mcp/McpHub"
-
-export type ToolArgs = {
- cwd: string
- supportsComputerUse: boolean
- diffStrategy?: DiffStrategy
- browserViewportSize?: string
- mcpHub?: McpHub
- toolOptions?: any
- partialReadsEnabled?: boolean
- settings?: Record
- experiments?: Record
-}
diff --git a/src/core/prompts/tools/update-todo-list.ts b/src/core/prompts/tools/update-todo-list.ts
deleted file mode 100644
index 30100617df..0000000000
--- a/src/core/prompts/tools/update-todo-list.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-import { ToolArgs } from "./types"
-
-/**
- * Get the description for the update_todo_list tool.
- */
-export function getUpdateTodoListDescription(args?: ToolArgs): string {
- return `## update_todo_list
-
-**Description:**
-Replace the entire TODO list with an updated checklist reflecting the current state. Always provide the full list; the system will overwrite the previous one. This tool is designed for step-by-step task tracking, allowing you to confirm completion of each step before updating, update multiple task statuses at once (e.g., mark one as completed and start the next), and dynamically add new todos discovered during long or complex tasks.
-
-**Checklist Format:**
-- Use a single-level markdown checklist (no nesting or subtasks).
-- List todos in the intended execution order.
-- Status options:
- - [ ] Task description (pending)
- - [x] Task description (completed)
- - [-] Task description (in progress)
-
-**Status Rules:**
-- [ ] = pending (not started)
-- [x] = completed (fully finished, no unresolved issues)
-- [-] = in_progress (currently being worked on)
-
-**Core Principles:**
-- Before updating, always confirm which todos have been completed since the last update.
-- You may update multiple statuses in a single update (e.g., mark the previous as completed and the next as in progress).
-- When a new actionable item is discovered during a long or complex task, add it to the todo list immediately.
-- Do not remove any unfinished todos unless explicitly instructed.
-- Always retain all unfinished tasks, updating their status as needed.
-- Only mark a task as completed when it is fully accomplished (no partials, no unresolved dependencies).
-- If a task is blocked, keep it as in_progress and add a new todo describing what needs to be resolved.
-- Remove tasks only if they are no longer relevant or if the user requests deletion.
-
-**Usage Example:**
-
-
-[x] Analyze requirements
-[x] Design architecture
-[-] Implement core logic
-[ ] Write tests
-[ ] Update documentation
-
-
-
-*After completing "Implement core logic" and starting "Write tests":*
-
-
-[x] Analyze requirements
-[x] Design architecture
-[x] Implement core logic
-[-] Write tests
-[ ] Update documentation
-[ ] Add performance benchmarks
-
-
-
-**When to Use:**
-- The task is complicated or involves multiple steps or requires ongoing tracking.
-- You need to update the status of several todos at once.
-- New actionable items are discovered during task execution.
-- The user requests a todo list or provides multiple tasks.
-- The task is complex and benefits from clear, stepwise progress tracking.
-
-**When NOT to Use:**
-- There is only a single, trivial task.
-- The task can be completed in one or two simple steps.
-- The request is purely conversational or informational.
-
-**Task Management Guidelines:**
-- Mark task as completed immediately after all work of the current task is done.
-- Start the next task by marking it as in_progress.
-- Add new todos as soon as they are identified.
-- Use clear, descriptive task names.
-`
-}
diff --git a/src/core/prompts/tools/use-mcp-tool.ts b/src/core/prompts/tools/use-mcp-tool.ts
deleted file mode 100644
index ac9ef5b075..0000000000
--- a/src/core/prompts/tools/use-mcp-tool.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { ToolArgs } from "./types"
-
-export function getUseMcpToolDescription(args: ToolArgs): string | undefined {
- if (!args.mcpHub) {
- return undefined
- }
- return `## use_mcp_tool
-Description: Request to use a tool provided by a connected MCP server. Each MCP server can provide multiple tools with different capabilities. Tools have defined input schemas that specify required and optional parameters.
-Parameters:
-- server_name: (required) The name of the MCP server providing the tool
-- tool_name: (required) The name of the tool to execute
-- arguments: (required) A JSON object containing the tool's input parameters, following the tool's input schema
-Usage:
-
-server name here
-tool name here
-
-{
- "param1": "value1",
- "param2": "value2"
-}
-
-
-
-Example: Requesting to use an MCP tool
-
-
-weather-server
-get_forecast
-
-{
- "city": "San Francisco",
- "days": 5
-}
-
-`
-}
diff --git a/src/core/prompts/tools/write-to-file.ts b/src/core/prompts/tools/write-to-file.ts
deleted file mode 100644
index 49ca1169f1..0000000000
--- a/src/core/prompts/tools/write-to-file.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { ToolArgs } from "./types"
-
-export function getWriteToFileDescription(args: ToolArgs): string {
- return `## write_to_file
-Description: Request to write content to a file. This tool is primarily used for **creating new files** or for scenarios where a **complete rewrite of an existing file is intentionally required**. If the file exists, it will be overwritten. If it doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
-
-**Important:** You should prefer using other editing tools over write_to_file when making changes to existing files, since write_to_file is slower and cannot handle large files. Use write_to_file primarily for new file creation.
-
-When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code.
-
-When creating a new project, organize all new files within a dedicated project directory unless the user specifies otherwise. Structure the project logically, adhering to best practices for the specific type of project being created.
-
-Parameters:
-- path: (required) The path of the file to write to (relative to the current workspace directory ${args.cwd})
-- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.
-
-Usage:
-
-File path here
-
-Your file content here
-
-
-
-Example: Writing a configuration file
-
-frontend-config.json
-
-{
- "apiEndpoint": "https://api.example.com",
- "theme": {
- "primaryColor": "#007bff",
- "secondaryColor": "#6c757d",
- "fontFamily": "Arial, sans-serif"
- },
- "features": {
- "darkMode": true,
- "notifications": true,
- "analytics": false
- },
- "version": "1.0.0"
-}
-
-`
-}
diff --git a/src/core/prompts/types.ts b/src/core/prompts/types.ts
index 0e27910c01..d438735f27 100644
--- a/src/core/prompts/types.ts
+++ b/src/core/prompts/types.ts
@@ -1,5 +1,3 @@
-import { ToolProtocol } from "@roo-code/types"
-
/**
* Settings passed to system prompt generation functions
*/
@@ -11,7 +9,6 @@ export interface SystemPromptSettings {
/** When true, recursively discover and load .roo/rules from subdirectories */
enableSubfolderRules?: boolean
newTaskRequireTodos: boolean
- toolProtocol?: ToolProtocol
/** When true, model should hide vendor/company identity in responses */
isStealthModel?: boolean
}
diff --git a/src/core/task-persistence/taskMetadata.ts b/src/core/task-persistence/taskMetadata.ts
index cf8d9adb52..4b77126971 100644
--- a/src/core/task-persistence/taskMetadata.ts
+++ b/src/core/task-persistence/taskMetadata.ts
@@ -1,7 +1,7 @@
import NodeCache from "node-cache"
import getFolderSize from "get-folder-size"
-import type { ClineMessage, HistoryItem, ToolProtocol } from "@roo-code/types"
+import type { ClineMessage, HistoryItem } from "@roo-code/types"
import { combineApiRequests } from "../../shared/combineApiRequests"
import { combineCommandSequences } from "../../shared/combineCommandSequences"
@@ -25,11 +25,6 @@ export type TaskMetadataOptions = {
apiConfigName?: 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({
@@ -43,7 +38,6 @@ export async function taskMetadata({
mode,
apiConfigName,
initialStatus,
- toolProtocol,
}: TaskMetadataOptions) {
const taskDir = await getTaskDirectoryPath(globalStoragePath, id)
@@ -99,8 +93,6 @@ 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,
@@ -118,7 +110,6 @@ export async function taskMetadata({
size: taskDirSize,
workspace,
mode,
- ...(toolProtocol && { toolProtocol }),
...(typeof apiConfigName === "string" && apiConfigName.length > 0 ? { apiConfigName } : {}),
...(initialStatus && { status: initialStatus }),
}
diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts
index 768ce83fc1..3b9b0bb2bc 100644
--- a/src/core/task/Task.ts
+++ b/src/core/task/Task.ts
@@ -33,7 +33,6 @@ import {
type HistoryItem,
type CreateTaskOptions,
type ModelInfo,
- type ToolProtocol,
type ClineApiReqCancelReason,
type ClineApiReqInfo,
RooCodeEventName,
@@ -45,20 +44,17 @@ import {
isIdleAsk,
isInteractiveAsk,
isResumableAsk,
- isNativeProtocol,
QueuedMessage,
DEFAULT_CONSECUTIVE_MISTAKE_LIMIT,
DEFAULT_CHECKPOINT_TIMEOUT_SECONDS,
MAX_CHECKPOINT_TIMEOUT_SECONDS,
MIN_CHECKPOINT_TIMEOUT_SECONDS,
- TOOL_PROTOCOL,
ConsecutiveMistakeError,
MAX_MCP_TOOLS_THRESHOLD,
countEnabledMcpTools,
} from "@roo-code/types"
import { TelemetryService } from "@roo-code/telemetry"
import { CloudService, BridgeOrchestrator } from "@roo-code/cloud"
-import { resolveToolProtocol, detectToolProtocolFromHistory } from "../../utils/resolveToolProtocol"
// api
import { ApiHandler, ApiHandlerCreateMessageMetadata, buildApiHandler } from "../../api"
@@ -107,7 +103,6 @@ import { FileContextTracker } from "../context-tracking/FileContextTracker"
import { RooIgnoreController } from "../ignore/RooIgnoreController"
import { RooProtectedController } from "../protect/RooProtectedController"
import { type AssistantMessageContent, presentAssistantMessage } from "../assistant-message"
-import { AssistantMessageParser } from "../assistant-message/AssistantMessageParser"
import { NativeToolCallParser } from "../assistant-message/NativeToolCallParser"
import { manageContext, willManageContext } from "../context-management"
import { ClineProvider } from "../webview/ClineProvider"
@@ -210,30 +205,6 @@ export class Task extends EventEmitter implements TaskLike {
*/
private _taskMode: string | undefined
- /**
- * The tool protocol locked to this task. Once set, the task will continue
- * using this protocol even if user settings change.
- *
- * ## Why This Matters
- * When NTC (Native Tool Calling) is enabled, XML parsing does NOT occur.
- * If a task previously used XML tools, resuming it with NTC enabled would
- * break because the tool calls in the history would not be parseable.
- *
- * ## Lifecycle
- *
- * ### For new tasks:
- * 1. Set immediately in constructor via `resolveToolProtocol()`
- * 2. Locked for the lifetime of the task
- *
- * ### For history items:
- * 1. If `historyItem.toolProtocol` exists, use it
- * 2. Otherwise, detect from API history via `detectToolProtocolFromHistory()`
- * 3. If no tools in history, use `resolveToolProtocol()` from current settings
- *
- * @private
- */
- private _taskToolProtocol: ToolProtocol | undefined
-
/**
* Promise that resolves when the task mode has been initialized.
* This ensures async mode initialization completes before the task is used.
@@ -389,7 +360,7 @@ export class Task extends EventEmitter implements TaskLike {
/**
* Push a tool_result block to userMessageContent, preventing duplicates.
- * This is critical for native tool protocol where duplicate tool_use_ids cause API errors.
+ * Duplicate tool_use_ids cause API errors.
*
* @param toolResult - The tool_result block to add
* @returns true if added, false if duplicate was skipped
@@ -412,7 +383,8 @@ export class Task extends EventEmitter implements TaskLike {
didAlreadyUseTool = false
didToolFailInCurrentTurn = false
didCompleteReadingStream = false
- assistantMessageParser?: AssistantMessageParser
+ // No streaming parser is required.
+ assistantMessageParser?: undefined
private providerProfileChangeListener?: (config: { name: string; provider?: string }) => void
// Native tool call streaming state (track which index each tool is at)
@@ -560,10 +532,6 @@ export class Task extends EventEmitter implements TaskLike {
this.taskModeReady = Promise.resolve()
this.taskApiConfigReady = Promise.resolve()
TelemetryService.instance.captureTaskRestarted(this.taskId)
-
- // For history items, use the persisted tool protocol if available.
- // If not available (old tasks), it will be detected in resumeTaskFromHistory.
- this._taskToolProtocol = historyItem.toolProtocol
} else {
// For new tasks, don't set the mode/apiConfigName yet - wait for async initialization.
this._taskMode = undefined
@@ -571,26 +539,15 @@ export class Task extends EventEmitter implements TaskLike {
this.taskModeReady = this.initializeTaskMode(provider)
this.taskApiConfigReady = this.initializeTaskApiConfigName(provider)
TelemetryService.instance.captureTaskCreated(this.taskId)
-
- // For new tasks, resolve and lock the tool protocol immediately.
- // This ensures the task will continue using this protocol even if
- // user settings change.
- const modelInfo = this.api.getModel().info
- this._taskToolProtocol = resolveToolProtocol(this.apiConfiguration, modelInfo)
}
- // Initialize the assistant message parser based on the locked tool protocol.
- // For native protocol, tool calls come as tool_call chunks, not XML.
- // For history items without a persisted protocol, we default to XML parser
- // and will update it in resumeTaskFromHistory after detection.
- const effectiveProtocol = this._taskToolProtocol || "xml"
- this.assistantMessageParser = effectiveProtocol !== "native" ? new AssistantMessageParser() : undefined
+ this.assistantMessageParser = undefined
this.messageQueueService = new MessageQueueService()
this.messageQueueStateChangedHandler = () => {
this.emit(RooCodeEventName.TaskUserMessage, this.taskId)
- this.providerRef.deref()?.postStateToWebview()
+ this.providerRef.deref()?.postStateToWebviewWithoutTaskHistory()
}
this.messageQueueService.on("stateChanged", this.messageQueueStateChangedHandler)
@@ -734,8 +691,7 @@ export class Task extends EventEmitter implements TaskLike {
}
/**
- * Sets up a listener for provider profile changes to automatically update the parser state.
- * This ensures the XML/native protocol parser stays synchronized with the current model.
+ * Sets up a listener for provider profile changes.
*
* @private
* @param provider - The ClineProvider instance to listen to
@@ -1080,7 +1036,7 @@ export class Task extends EventEmitter implements TaskLike {
/**
* Flush any pending tool results to the API conversation history.
*
- * This is critical for native tool protocol when the task is about to be
+ * This is critical when the task is about to be
* delegated (e.g., via new_task). Before delegation, if other tools were
* called in the same turn before new_task, their tool_result blocks are
* accumulated in `userMessageContent` but haven't been saved to the API
@@ -1137,7 +1093,9 @@ export class Task extends EventEmitter implements TaskLike {
private async addToClineMessages(message: ClineMessage) {
this.clineMessages.push(message)
const provider = this.providerRef.deref()
- await provider?.postStateToWebview()
+ // Avoid resending large, mostly-static fields (notably taskHistory) on every chat message update.
+ // taskHistory is maintained in-memory in the webview and updated via taskHistoryItemUpdated.
+ await provider?.postStateToWebviewWithoutTaskHistory()
this.emit(RooCodeEventName.Message, { action: "created", message })
await this.saveClineMessages()
@@ -1210,7 +1168,6 @@ export class Task extends EventEmitter implements TaskLike {
mode: this._taskMode || defaultModeSlug, // Use the task's own mode, not the current provider mode.
apiConfigName: this._taskApiConfigName, // Use the task's own provider profile, not the current provider profile.
initialStatus: this.initialStatus,
- toolProtocol: this._taskToolProtocol, // Persist the locked tool protocol.
})
// Emit token/tool usage updates using debounced function
@@ -1541,9 +1498,8 @@ export class Task extends EventEmitter implements TaskLike {
}
/**
- * Updates the API configuration but preserves the locked tool protocol.
- * The task's tool protocol is locked at creation time and should NOT change
- * even when switching between models/profiles with different settings.
+ * Updates the API configuration and rebuilds the API handler.
+ * There is no tool-protocol switching or tool parser swapping.
*
* @param newApiConfiguration - The new API configuration to use
*/
@@ -1551,11 +1507,6 @@ export class Task extends EventEmitter implements TaskLike {
// Update the configuration and rebuild the API handler
this.apiConfiguration = newApiConfiguration
this.api = buildApiHandler(this.apiConfiguration)
-
- // IMPORTANT: Do NOT change the parser based on the new configuration!
- // The task's tool protocol is locked at creation time and must remain
- // consistent throughout the task's lifetime to ensure history can be
- // properly resumed.
}
public async submitUserMessage(
@@ -1592,7 +1543,10 @@ export class Task extends EventEmitter implements TaskLike {
this.emit(RooCodeEventName.TaskUserMessage, this.taskId)
- provider.postMessageToWebview({ type: "invoke", invoke: "sendMessage", text, images })
+ // Handle the message directly instead of routing through the webview.
+ // This avoids a race condition where the webview's message state hasn't
+ // hydrated yet, causing it to interpret the message as a new task request.
+ this.handleWebviewAskResponse("messageResponse", text, images)
} else {
console.error("[Task#submitUserMessage] Provider reference lost")
}
@@ -1618,33 +1572,9 @@ export class Task extends EventEmitter implements TaskLike {
// Get condensing configuration
const state = await this.providerRef.deref()?.getState()
- // These properties may not exist in the state type yet, but are used for condensing configuration
- const customCondensingPrompt = state?.customCondensingPrompt
- const condensingApiConfigId = state?.condensingApiConfigId
- const listApiConfigMeta = state?.listApiConfigMeta
-
- // Determine API handler to use
- let condensingApiHandler: ApiHandler | undefined
- if (condensingApiConfigId && listApiConfigMeta && Array.isArray(listApiConfigMeta)) {
- // Find matching config by ID
- const matchingConfig = listApiConfigMeta.find((config) => config.id === condensingApiConfigId)
- if (matchingConfig) {
- const profile = await this.providerRef.deref()?.providerSettingsManager.getProfile({
- id: condensingApiConfigId,
- })
- // Ensure profile and apiProvider exist before trying to build handler
- if (profile && profile.apiProvider) {
- condensingApiHandler = buildApiHandler(profile)
- }
- }
- }
+ const customCondensingPrompt = state?.customSupportPrompts?.CONDENSE
const { contextTokens: prevContextTokens } = this.getTokenUsage()
-
- // Determine if we're using native tool protocol for proper message handling
- // Use the task's locked protocol, NOT the current settings (fallback to xml if not set)
- const useNativeTools = isNativeProtocol(this._taskToolProtocol ?? "xml")
-
const {
messages,
summary,
@@ -1660,8 +1590,6 @@ export class Task extends EventEmitter implements TaskLike {
prevContextTokens,
false, // manual trigger
customCondensingPrompt, // User's custom prompt
- condensingApiHandler, // Specific handler for condensing
- useNativeTools, // Pass native tools flag for proper message handling
)
if (error) {
this.say(
@@ -1825,10 +1753,7 @@ export class Task extends EventEmitter implements TaskLike {
relPath ? ` for '${relPath.toPosix()}'` : ""
} without value for required parameter '${paramName}'. Retrying...`,
)
- // Use the task's locked protocol, NOT the current settings (fallback to xml if not set)
- return formatResponse.toolError(
- formatResponse.missingToolParameterError(paramName, this._taskToolProtocol ?? "xml"),
- )
+ return formatResponse.toolError(formatResponse.missingToolParameterError(paramName))
}
// Lifecycle
@@ -1866,69 +1791,77 @@ export class Task extends EventEmitter implements TaskLike {
}
private async startTask(task?: string, images?: string[]): Promise {
- if (this.enableBridge) {
- try {
- await BridgeOrchestrator.subscribeToTask(this)
- } catch (error) {
- console.error(
- `[Task#startTask] BridgeOrchestrator.subscribeToTask() failed: ${error instanceof Error ? error.message : String(error)}`,
+ try {
+ if (this.enableBridge) {
+ try {
+ await BridgeOrchestrator.subscribeToTask(this)
+ } catch (error) {
+ console.error(
+ `[Task#startTask] BridgeOrchestrator.subscribeToTask() failed: ${error instanceof Error ? error.message : String(error)}`,
+ )
+ }
+ }
+
+ // `conversationHistory` (for API) and `clineMessages` (for webview)
+ // need to be in sync.
+ // If the extension process were killed, then on restart the
+ // `clineMessages` might not be empty, so we need to set it to [] when
+ // we create a new Cline client (otherwise webview would show stale
+ // messages from previous session).
+ this.clineMessages = []
+ this.apiConversationHistory = []
+
+ // The todo list is already set in the constructor if initialTodos were provided
+ // No need to add any messages - the todoList property is already set
+
+ await this.providerRef.deref()?.postStateToWebviewWithoutTaskHistory()
+
+ await this.say("text", task, images)
+
+ // Check for too many MCP tools and warn the user
+ const { enabledToolCount, enabledServerCount } = await this.getEnabledMcpToolsCount()
+ if (enabledToolCount > MAX_MCP_TOOLS_THRESHOLD) {
+ await this.say(
+ "too_many_tools_warning",
+ JSON.stringify({
+ toolCount: enabledToolCount,
+ serverCount: enabledServerCount,
+ threshold: MAX_MCP_TOOLS_THRESHOLD,
+ }),
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ { isNonInteractive: true },
)
}
- }
+ this.isInitialized = true
- // `conversationHistory` (for API) and `clineMessages` (for webview)
- // need to be in sync.
- // If the extension process were killed, then on restart the
- // `clineMessages` might not be empty, so we need to set it to [] when
- // we create a new Cline client (otherwise webview would show stale
- // messages from previous session).
- this.clineMessages = []
- this.apiConversationHistory = []
+ const imageBlocks: Anthropic.ImageBlockParam[] = formatResponse.imageBlocks(images)
- // The todo list is already set in the constructor if initialTodos were provided
- // No need to add any messages - the todoList property is already set
-
- await this.providerRef.deref()?.postStateToWebview()
-
- await this.say("text", task, images)
-
- // Check for too many MCP tools and warn the user
- const { enabledToolCount, enabledServerCount } = await this.getEnabledMcpToolsCount()
- if (enabledToolCount > MAX_MCP_TOOLS_THRESHOLD) {
- await this.say(
- "too_many_tools_warning",
- JSON.stringify({
- toolCount: enabledToolCount,
- serverCount: enabledServerCount,
- threshold: MAX_MCP_TOOLS_THRESHOLD,
- }),
- undefined,
- undefined,
- undefined,
- undefined,
- { isNonInteractive: true },
- )
- }
- this.isInitialized = true
-
- let imageBlocks: Anthropic.ImageBlockParam[] = formatResponse.imageBlocks(images)
-
- // Task starting
-
- await this.initiateTaskLoop([
- {
- type: "text",
- text: `\n${task}\n`,
- },
- ...imageBlocks,
- ]).catch((error) => {
- // Swallow loop rejection when the task was intentionally abandoned/aborted
- // during delegation or user cancellation to prevent unhandled rejections.
- if (this.abandoned === true || this.abortReason === "user_cancelled") {
+ // Task starting
+ await this.initiateTaskLoop([
+ {
+ type: "text",
+ text: `\n${task}\n`,
+ },
+ ...imageBlocks,
+ ]).catch((error) => {
+ // Swallow loop rejection when the task was intentionally abandoned/aborted
+ // during delegation or user cancellation to prevent unhandled rejections.
+ if (this.abandoned === true || this.abortReason === "user_cancelled") {
+ return
+ }
+ throw error
+ })
+ } catch (error) {
+ // In tests and some UX flows, tasks can be aborted while `startTask` is still
+ // initializing. Treat abort/abandon as expected and avoid unhandled rejections.
+ if (this.abandoned === true || this.abort === true || this.abortReason === "user_cancelled") {
return
}
throw error
- })
+ }
}
private async resumeTaskFromHistory() {
@@ -1993,31 +1926,6 @@ export class Task extends EventEmitter implements TaskLike {
// the task first.
this.apiConversationHistory = await this.getSavedApiConversationHistory()
- // If we don't have a persisted tool protocol (old tasks before this feature),
- // detect it from the API history. This ensures tasks that previously used
- // XML tools will continue using XML even if NTC is now enabled.
- if (!this._taskToolProtocol) {
- const detectedProtocol = detectToolProtocolFromHistory(this.apiConversationHistory)
- if (detectedProtocol) {
- // Found tool calls in history - lock to that protocol
- this._taskToolProtocol = detectedProtocol
- } else {
- // No tool calls in history yet - use current settings
- const modelInfo = this.api.getModel().info
- this._taskToolProtocol = resolveToolProtocol(this.apiConfiguration, modelInfo)
- }
-
- // Update parser state to match the detected/resolved protocol
- const shouldUseXmlParser = this._taskToolProtocol === "xml"
- if (shouldUseXmlParser && !this.assistantMessageParser) {
- this.assistantMessageParser = new AssistantMessageParser()
- } else if (!shouldUseXmlParser && this.assistantMessageParser) {
- this.assistantMessageParser.reset()
- this.assistantMessageParser = undefined
- }
- } else {
- }
-
const lastClineMessage = this.clineMessages
.slice()
.reverse()
@@ -2047,50 +1955,7 @@ export class Task extends EventEmitter implements TaskLike {
// even if it goes out of sync with cline messages.
let existingApiConversationHistory: ApiMessage[] = await this.getSavedApiConversationHistory()
- // v2.0 xml tags refactor caveat: since we don't use tools anymore for XML protocol,
- // we need to replace all tool use blocks with a text block since the API disallows
- // conversations with tool uses and no tool schema.
- // For native protocol, we preserve tool_use and tool_result blocks as they're expected by the API.
- // IMPORTANT: Use the task's locked protocol, NOT the current settings!
- const useNative = isNativeProtocol(this._taskToolProtocol)
-
- // Only convert tool blocks to text for XML protocol
- // For native protocol, the API expects proper tool_use/tool_result structure
- if (!useNative) {
- const conversationWithoutToolBlocks = existingApiConversationHistory.map((message) => {
- if (Array.isArray(message.content)) {
- const newContent = message.content.map((block) => {
- if (block.type === "tool_use") {
- // Format tool invocation based on the task's locked protocol
- const params = block.input as Record
- const formattedText = formatToolInvocation(block.name, params, this._taskToolProtocol)
-
- return {
- type: "text",
- text: formattedText,
- } as Anthropic.Messages.TextBlockParam
- } else if (block.type === "tool_result") {
- // Convert block.content to text block array, removing images
- const contentAsTextBlocks = Array.isArray(block.content)
- ? block.content.filter((item) => item.type === "text")
- : [{ type: "text", text: block.content }]
- const textContent = contentAsTextBlocks.map((item) => item.text).join("\n\n")
- const toolName = findToolName(block.tool_use_id, existingApiConversationHistory)
- return {
- type: "text",
- text: `[${toolName} Result]\n\n${textContent}`,
- } as Anthropic.Messages.TextBlockParam
- }
- return block
- })
- return { ...message, content: newContent }
- }
- return message
- })
- existingApiConversationHistory = conversationWithoutToolBlocks
- }
-
- // FIXME: remove tool use blocks altogether
+ // Tool blocks are always preserved; native tool calling only.
// if the last message is an assistant message, we need to check if there's tool use since every tool use has to have a tool response
// if there's no tool use and only a text block, then we can just add a user message
@@ -2509,8 +2374,7 @@ export class Task extends EventEmitter implements TaskLike {
// the user hits max requests and denies resetting the count.
break
} else {
- // Use the task's locked protocol, NOT the current settings (fallback to xml if not set)
- nextUserContent = [{ type: "text", text: formatResponse.noToolsUsed(this._taskToolProtocol ?? "xml") }]
+ nextUserContent = [{ type: "text", text: formatResponse.noToolsUsed() }]
}
}
}
@@ -2634,7 +2498,7 @@ export class Task extends EventEmitter implements TaskLike {
const environmentDetails = await getEnvironmentDetails(this, currentIncludeFileDetails)
// Remove any existing environment_details blocks before adding fresh ones.
- // This prevents duplicate environment details when resuming tasks with XML tool calls,
+ // This prevents duplicate environment details when resuming tasks,
// where the old user message content may already contain environment details from the previous session.
// We check for both opening and closing tags to ensure we're matching complete environment detail blocks,
// not just mentions of the tag in regular content.
@@ -2678,7 +2542,7 @@ export class Task extends EventEmitter implements TaskLike {
} satisfies ClineApiReqInfo)
await this.saveClineMessages()
- await this.providerRef.deref()?.postStateToWebview()
+ await this.providerRef.deref()?.postStateToWebviewWithoutTaskHistory()
try {
let cacheWriteTokens = 0
@@ -2773,7 +2637,7 @@ export class Task extends EventEmitter implements TaskLike {
this.didToolFailInCurrentTurn = false
this.presentAssistantMessageLocked = false
this.presentAssistantMessageHasPendingUpdates = false
- this.assistantMessageParser?.reset()
+ // No legacy text-stream tool parser.
this.streamingToolCallIndices.clear()
// Clear any leftover streaming tool call state from previous interrupted streams
NativeToolCallParser.clearAllStreamingToolCalls()
@@ -2786,15 +2650,6 @@ export class Task extends EventEmitter implements TaskLike {
this.cachedStreamingModel = this.api.getModel()
const streamModelInfo = this.cachedStreamingModel.info
const cachedModelId = this.cachedStreamingModel.id
- // Use the task's locked protocol instead of resolving fresh.
- // This ensures task resumption works correctly even if NTC settings changed.
- // Fallback to resolving if somehow _taskToolProtocol is not set (should not happen).
- const streamProtocol = resolveToolProtocol(
- this.apiConfiguration,
- streamModelInfo,
- this._taskToolProtocol,
- )
- const shouldUseXmlParser = streamProtocol === "xml"
// Yields only if the first chunk is successful, otherwise will
// allow the user to retry the request (most likely due to rate
@@ -2973,8 +2828,8 @@ export class Task extends EventEmitter implements TaskLike {
presentAssistantMessage(this)
} else if (toolUseIndex !== undefined) {
// finalizeStreamingToolCall returned null (malformed JSON or missing args)
- // We still need to mark the tool as non-partial so it gets executed
- // The tool's validation will catch any missing required parameters
+ // Mark the tool as non-partial so it's presented as complete, but execution
+ // will be short-circuited in presentAssistantMessage with a structured tool_result.
const existingToolUse = this.assistantMessageContent[toolUseIndex]
if (existingToolUse && existingToolUse.type === "tool_use") {
existingToolUse.partial = false
@@ -3028,43 +2883,20 @@ export class Task extends EventEmitter implements TaskLike {
case "text": {
assistantMessage += chunk.text
- // Use the protocol determined at the start of streaming
- // Don't rely solely on parser existence - parser might exist from previous state
- if (shouldUseXmlParser && this.assistantMessageParser) {
- // XML protocol: Parse raw assistant message chunk into content blocks
- const prevLength = this.assistantMessageContent.length
- this.assistantMessageContent = this.assistantMessageParser.processChunk(chunk.text)
-
- if (this.assistantMessageContent.length > prevLength) {
- // New content we need to present, reset to
- // false in case previous content set this to true.
- this.userMessageContentReady = false
- }
-
- // Present content to user.
- presentAssistantMessage(this)
+ // Native tool calling: text chunks are plain text.
+ // Create or update a text content block directly
+ const lastBlock = this.assistantMessageContent[this.assistantMessageContent.length - 1]
+ if (lastBlock?.type === "text" && lastBlock.partial) {
+ lastBlock.content = assistantMessage
} else {
- // Native protocol: Text chunks are plain text, not XML tool calls
- // Create or update a text content block directly
- const lastBlock =
- this.assistantMessageContent[this.assistantMessageContent.length - 1]
-
- if (lastBlock?.type === "text" && lastBlock.partial) {
- // Update existing partial text block
- lastBlock.content = assistantMessage
- } else {
- // Create new text block
- this.assistantMessageContent.push({
- type: "text",
- content: assistantMessage,
- partial: true,
- })
- this.userMessageContentReady = false
- }
-
- // Present content to user
- presentAssistantMessage(this)
+ this.assistantMessageContent.push({
+ type: "text",
+ content: assistantMessage,
+ partial: true,
+ })
+ this.userMessageContentReady = false
}
+ presentAssistantMessage(this)
break
}
}
@@ -3406,19 +3238,11 @@ export class Task extends EventEmitter implements TaskLike {
// Can't just do this b/c a tool could be in the middle of executing.
// this.assistantMessageContent.forEach((e) => (e.partial = false))
- // Now that the stream is complete, finalize any remaining partial content blocks (XML protocol only)
- // Use the protocol determined at the start of streaming
- if (shouldUseXmlParser && this.assistantMessageParser) {
- this.assistantMessageParser.finalizeContentBlocks()
- const parsedBlocks = this.assistantMessageParser.getContentBlocks()
- // For XML protocol: Use only parsed blocks (includes both text and tool_use parsed from XML)
- this.assistantMessageContent = parsedBlocks
- }
+ // No legacy streaming parser to finalize.
- // Present any partial blocks that were just completed
- // For XML protocol: includes both text and tool_use blocks parsed from the text stream
- // For native protocol: tool_use blocks were already presented during streaming via
- // tool_call_partial events, but we still need to present them if they exist (e.g., malformed)
+ // Present any partial blocks that were just completed.
+ // Tool calls are typically presented during streaming via tool_call_partial events,
+ // but we still present here if any partial blocks remain (e.g., malformed streams).
if (partialBlocks.length > 0) {
// If there is content to update then it will complete and
// update `this.userMessageContentReady` to true, which we
@@ -3446,10 +3270,9 @@ export class Task extends EventEmitter implements TaskLike {
}
await this.saveClineMessages()
- await this.providerRef.deref()?.postStateToWebview()
+ await this.providerRef.deref()?.postStateToWebviewWithoutTaskHistory()
- // Reset parser after each complete conversation round (XML protocol only)
- this.assistantMessageParser?.reset()
+ // No legacy text-stream tool parser state to reset.
// Now add to apiConversationHistory.
// Need to save assistant responses to file before proceeding to
@@ -3596,7 +3419,7 @@ export class Task extends EventEmitter implements TaskLike {
// Use the task's locked protocol for consistent behavior
this.userMessageContent.push({
type: "text",
- text: formatResponse.noToolsUsed(this._taskToolProtocol ?? "xml"),
+ text: formatResponse.noToolsUsed(),
})
} else {
// Reset counter when tools are used successfully
@@ -3630,13 +3453,12 @@ export class Task extends EventEmitter implements TaskLike {
await this.say("error", "MODEL_NO_ASSISTANT_MESSAGES")
}
- // IMPORTANT: For native tool protocol, we already added the user message to
+ // IMPORTANT: We already added the user message to
// 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()
- // Use the task's locked protocol, NOT current settings
- if (isNativeProtocol(this._taskToolProtocol ?? "xml") && this.apiConversationHistory.length > 0) {
+ if (this.apiConversationHistory.length > 0) {
const lastMessage = this.apiConversationHistory[this.apiConversationHistory.length - 1]
if (lastMessage.role === "user") {
// Remove the last user message that we added earlier
@@ -3695,14 +3517,11 @@ export class Task extends EventEmitter implements TaskLike {
continue
} else {
// User declined to retry
- // For native protocol, re-add the user message we removed
- // Use the task's locked protocol, NOT current settings
- if (isNativeProtocol(this._taskToolProtocol ?? "xml")) {
- await this.addToApiConversationHistory({
- role: "user",
- content: currentUserContent,
- })
- }
+ // Re-add the user message we removed.
+ await this.addToApiConversationHistory({
+ role: "user",
+ content: currentUserContent,
+ })
await this.say(
"error",
@@ -3795,15 +3614,6 @@ export class Task extends EventEmitter implements TaskLike {
const canUseBrowserTool = modelSupportsBrowser && modeSupportsBrowser && (browserToolEnabled ?? true)
- // Use the task's locked protocol for system prompt consistency.
- // This ensures the system prompt matches the protocol the task was started with,
- // even if user settings have changed since then.
- const toolProtocol = resolveToolProtocol(
- apiConfiguration ?? this.apiConfiguration,
- modelInfo,
- this._taskToolProtocol,
- )
-
return SYSTEM_PROMPT(
provider.context,
this.cwd,
@@ -3831,7 +3641,6 @@ export class Task extends EventEmitter implements TaskLike {
newTaskRequireTodos: vscode.workspace
.getConfiguration(Package.name)
.get("newTaskRequireTodos", false),
- toolProtocol,
isStealthModel: modelInfo?.isStealthModel,
},
undefined, // todoList
@@ -3872,11 +3681,6 @@ export class Task extends EventEmitter implements TaskLike {
`Current tokens: ${contextTokens}, Context window: ${contextWindow}. ` +
`Forcing truncation to ${FORCED_CONTEXT_REDUCTION_PERCENT}% of current context.`,
)
-
- // Determine if we're using native tool protocol for proper message handling
- // Use the task's locked protocol, NOT the current settings
- const useNativeTools = isNativeProtocol(this._taskToolProtocol ?? "xml")
-
// Send condenseTaskContextStarted to show in-progress indicator
await this.providerRef.deref()?.postMessageToWebview({ type: "condenseTaskContextStarted", text: this.taskId })
@@ -3893,7 +3697,6 @@ export class Task extends EventEmitter implements TaskLike {
taskId: this.taskId,
profileThresholds,
currentProfileId,
- useNativeTools,
})
if (truncateResult.messages !== this.apiConversationHistory) {
@@ -3989,28 +3792,7 @@ export class Task extends EventEmitter implements TaskLike {
} = state ?? {}
// Get condensing configuration for automatic triggers.
- const customCondensingPrompt = state?.customCondensingPrompt
- const condensingApiConfigId = state?.condensingApiConfigId
- const listApiConfigMeta = state?.listApiConfigMeta
-
- // Determine API handler to use for condensing.
- let condensingApiHandler: ApiHandler | undefined
-
- if (condensingApiConfigId && listApiConfigMeta && Array.isArray(listApiConfigMeta)) {
- // Find matching config by ID
- const matchingConfig = listApiConfigMeta.find((config) => config.id === condensingApiConfigId)
-
- if (matchingConfig) {
- const profile = await this.providerRef.deref()?.providerSettingsManager.getProfile({
- id: condensingApiConfigId,
- })
-
- // Ensure profile and apiProvider exist before trying to build handler.
- if (profile && profile.apiProvider) {
- condensingApiHandler = buildApiHandler(profile)
- }
- }
- }
+ const customCondensingPrompt = state?.customSupportPrompts?.CONDENSE
if (!options.skipProviderRateLimit) {
await this.maybeWaitForProviderRateLimit(retryAttempt)
@@ -4041,11 +3823,6 @@ export class Task extends EventEmitter implements TaskLike {
// Get the current profile ID using the helper method
const currentProfileId = this.getCurrentProfileId(state)
-
- // Determine if we're using native tool protocol for proper message handling
- // Use the task's locked protocol, NOT the current settings
- const useNativeTools = isNativeProtocol(this._taskToolProtocol ?? "xml")
-
// Check if context management will likely run (threshold check)
// This allows us to show an in-progress indicator to the user
// We use the centralized willManageContext helper to avoid duplicating threshold logic
@@ -4089,10 +3866,8 @@ export class Task extends EventEmitter implements TaskLike {
systemPrompt,
taskId: this.taskId,
customCondensingPrompt,
- condensingApiHandler,
profileThresholds,
currentProfileId,
- useNativeTools,
})
if (truncateResult.messages !== this.apiConversationHistory) {
await this.overwriteApiConversationHistory(truncateResult.messages)
@@ -4168,14 +3943,8 @@ export class Task extends EventEmitter implements TaskLike {
throw new Error("Auto-approval limit reached and user did not approve continuation")
}
- // Determine if we should include native tools based on:
- // 1. Task's locked tool protocol is set to NATIVE
- // 2. Model supports native tools
- // CRITICAL: Use the task's locked protocol to ensure tasks that started with XML
- // tools continue using XML even if NTC settings have since changed.
+ // Whether we include tools is determined by whether we have any tools to send.
const modelInfo = this.api.getModel().info
- const taskProtocol = this._taskToolProtocol ?? "xml"
- const shouldIncludeTools = taskProtocol === TOOL_PROTOCOL.NATIVE && (modelInfo.supportsNativeTools ?? false)
// Build complete tools array: native tools + dynamic MCP tools
// When includeAllToolsWithRestrictions is true, returns all tools but provides
@@ -4191,7 +3960,7 @@ export class Task extends EventEmitter implements TaskLike {
// so they continue to receive only the filtered tools for the current mode.
const supportsAllowedFunctionNames = apiConfiguration?.apiProvider === "gemini"
- if (shouldIncludeTools) {
+ {
const provider = this.providerRef.deref()
if (!provider) {
throw new Error("Provider reference lost during tool building")
@@ -4215,6 +3984,8 @@ export class Task extends EventEmitter implements TaskLike {
allowedFunctionNames = toolsResult.allowedFunctionNames
}
+ const shouldIncludeTools = allTools.length > 0
+
// Resolve parallel tool calls setting from experiment (will move to per-API-profile setting later)
const parallelToolCallsEnabled = experiments.isEnabled(
state?.experiments ?? {},
@@ -4225,12 +3996,11 @@ export class Task extends EventEmitter implements TaskLike {
mode: mode,
taskId: this.taskId,
suppressPreviousResponseId: this.skipPrevResponseIdOnce,
- // Include tools and tool protocol when using native protocol and model supports it
+ // Include tools whenever they are present.
...(shouldIncludeTools
? {
tools: allTools,
tool_choice: "auto",
- toolProtocol: taskProtocol,
parallelToolCalls: parallelToolCallsEnabled,
// When mode restricts tools, provide allowedFunctionNames so providers
// like Gemini can see all tools in history but only call allowed ones
@@ -4640,16 +4410,6 @@ export class Task extends EventEmitter implements TaskLike {
return this.workspacePath
}
- /**
- * Get the tool protocol locked to this task.
- * Returns undefined only if the task hasn't been fully initialized yet.
- *
- * @see {@link _taskToolProtocol} for lifecycle details
- */
- public get taskToolProtocol() {
- return this._taskToolProtocol
- }
-
/**
* Provides convenient access to high-level message operations.
* Uses lazy initialization - the MessageManager is only created when first accessed.
diff --git a/src/core/task/__tests__/Task.spec.ts b/src/core/task/__tests__/Task.spec.ts
index 6064ed965e..870cdc556e 100644
--- a/src/core/task/__tests__/Task.spec.ts
+++ b/src/core/task/__tests__/Task.spec.ts
@@ -282,6 +282,7 @@ describe("Cline", () => {
// Mock provider methods
mockProvider.postMessageToWebview = vi.fn().mockResolvedValue(undefined)
mockProvider.postStateToWebview = vi.fn().mockResolvedValue(undefined)
+ mockProvider.postStateToWebviewWithoutTaskHistory = vi.fn().mockResolvedValue(undefined)
mockProvider.getTaskWithId = vi.fn().mockImplementation(async (id) => ({
historyItem: {
id,
@@ -987,6 +988,7 @@ describe("Cline", () => {
getSkillsManager: vi.fn().mockReturnValue(undefined),
say: vi.fn(),
postStateToWebview: vi.fn().mockResolvedValue(undefined),
+ postStateToWebviewWithoutTaskHistory: vi.fn().mockResolvedValue(undefined),
postMessageToWebview: vi.fn().mockResolvedValue(undefined),
updateTaskHistory: vi.fn().mockResolvedValue(undefined),
}
@@ -1521,7 +1523,7 @@ describe("Cline", () => {
})
describe("submitUserMessage", () => {
- it("should always route through webview sendMessage invoke", async () => {
+ it("should call handleWebviewAskResponse directly", async () => {
const task = new Task({
provider: mockProvider,
apiConfiguration: mockApiConfig,
@@ -1529,6 +1531,9 @@ describe("Cline", () => {
startTask: false,
})
+ // Spy on handleWebviewAskResponse
+ const handleResponseSpy = vi.spyOn(task, "handleWebviewAskResponse")
+
// Set up some existing messages to simulate an ongoing conversation
task.clineMessages = [
{
@@ -1542,13 +1547,10 @@ describe("Cline", () => {
// Call submitUserMessage
task.submitUserMessage("test message", ["image1.png"])
- // Verify postMessageToWebview was called with sendMessage invoke
- expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith({
- type: "invoke",
- invoke: "sendMessage",
- text: "test message",
- images: ["image1.png"],
- })
+ // Verify handleWebviewAskResponse was called directly (not webview)
+ expect(handleResponseSpy).toHaveBeenCalledWith("messageResponse", "test message", ["image1.png"])
+ // Should NOT route through webview anymore
+ expect(mockProvider.postMessageToWebview).not.toHaveBeenCalled()
})
it("should handle empty messages gracefully", async () => {
@@ -1559,18 +1561,21 @@ describe("Cline", () => {
startTask: false,
})
+ // Spy on handleWebviewAskResponse
+ const handleResponseSpy = vi.spyOn(task, "handleWebviewAskResponse")
+
// Call with empty text and no images
task.submitUserMessage("", [])
- // Should not call postMessageToWebview for empty messages
- expect(mockProvider.postMessageToWebview).not.toHaveBeenCalled()
+ // Should not call handleWebviewAskResponse for empty messages
+ expect(handleResponseSpy).not.toHaveBeenCalled()
// Call with whitespace only
task.submitUserMessage(" ", [])
- expect(mockProvider.postMessageToWebview).not.toHaveBeenCalled()
+ expect(handleResponseSpy).not.toHaveBeenCalled()
})
- it("should route through webview for both new and existing tasks", async () => {
+ it("should call handleWebviewAskResponse for both new and existing task states", async () => {
const task = new Task({
provider: mockProvider,
apiConfiguration: mockApiConfig,
@@ -1578,19 +1583,17 @@ describe("Cline", () => {
startTask: false,
})
+ // Spy on handleWebviewAskResponse
+ const handleResponseSpy = vi.spyOn(task, "handleWebviewAskResponse")
+
// Test with no messages (new task scenario)
task.clineMessages = []
task.submitUserMessage("new task", ["image1.png"])
- expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith({
- type: "invoke",
- invoke: "sendMessage",
- text: "new task",
- images: ["image1.png"],
- })
+ expect(handleResponseSpy).toHaveBeenCalledWith("messageResponse", "new task", ["image1.png"])
// Clear mock
- mockProvider.postMessageToWebview.mockClear()
+ handleResponseSpy.mockClear()
// Test with existing messages (ongoing task scenario)
task.clineMessages = [
@@ -1603,12 +1606,7 @@ describe("Cline", () => {
]
task.submitUserMessage("follow-up message", ["image2.png"])
- expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith({
- type: "invoke",
- invoke: "sendMessage",
- text: "follow-up message",
- images: ["image2.png"],
- })
+ expect(handleResponseSpy).toHaveBeenCalledWith("messageResponse", "follow-up message", ["image2.png"])
})
it("should handle undefined provider gracefully", async () => {
@@ -1619,6 +1617,9 @@ describe("Cline", () => {
startTask: false,
})
+ // Spy on handleWebviewAskResponse
+ const handleResponseSpy = vi.spyOn(task, "handleWebviewAskResponse")
+
// Simulate weakref returning undefined
Object.defineProperty(task, "providerRef", {
value: { deref: () => undefined },
@@ -1633,7 +1634,7 @@ describe("Cline", () => {
task.submitUserMessage("test message")
expect(consoleErrorSpy).toHaveBeenCalledWith("[Task#submitUserMessage] Provider reference lost")
- expect(mockProvider.postMessageToWebview).not.toHaveBeenCalled()
+ expect(handleResponseSpy).not.toHaveBeenCalled()
// Restore console.error
consoleErrorSpy.mockRestore()
@@ -1901,6 +1902,7 @@ describe("Queued message processing after condense", () => {
const provider = new ClineProvider(ctx, output as any, "sidebar", new ContextProxy(ctx)) as any
provider.postMessageToWebview = vi.fn().mockResolvedValue(undefined)
provider.postStateToWebview = vi.fn().mockResolvedValue(undefined)
+ provider.postStateToWebviewWithoutTaskHistory = vi.fn().mockResolvedValue(undefined)
provider.getState = vi.fn().mockResolvedValue({})
return provider
}
@@ -2039,6 +2041,7 @@ describe("pushToolResultToUserContent", () => {
mockProvider.postMessageToWebview = vi.fn().mockResolvedValue(undefined)
mockProvider.postStateToWebview = vi.fn().mockResolvedValue(undefined)
+ mockProvider.postStateToWebviewWithoutTaskHistory = vi.fn().mockResolvedValue(undefined)
})
it("should add tool_result when not a duplicate", () => {
diff --git a/src/core/task/__tests__/Task.sticky-profile-race.spec.ts b/src/core/task/__tests__/Task.sticky-profile-race.spec.ts
index e78301541d..38a3098b04 100644
--- a/src/core/task/__tests__/Task.sticky-profile-race.spec.ts
+++ b/src/core/task/__tests__/Task.sticky-profile-race.spec.ts
@@ -121,6 +121,7 @@ describe("Task - sticky provider profile init race", () => {
on: vi.fn(),
off: vi.fn(),
postStateToWebview: vi.fn().mockResolvedValue(undefined),
+ postStateToWebviewWithoutTaskHistory: vi.fn().mockResolvedValue(undefined),
updateTaskHistory: vi.fn().mockResolvedValue(undefined),
} as unknown as ClineProvider
diff --git a/src/core/task/__tests__/Task.throttle.test.ts b/src/core/task/__tests__/Task.throttle.test.ts
index 1d5911be9f..904bc46b55 100644
--- a/src/core/task/__tests__/Task.throttle.test.ts
+++ b/src/core/task/__tests__/Task.throttle.test.ts
@@ -79,6 +79,7 @@ describe("Task token usage throttling", () => {
getState: vi.fn().mockResolvedValue({ mode: "code" }),
log: vi.fn(),
postStateToWebview: vi.fn().mockResolvedValue(undefined),
+ postStateToWebviewWithoutTaskHistory: vi.fn().mockResolvedValue(undefined),
updateTaskHistory: vi.fn().mockResolvedValue(undefined),
}
diff --git a/src/core/task/__tests__/flushPendingToolResultsToHistory.spec.ts b/src/core/task/__tests__/flushPendingToolResultsToHistory.spec.ts
index 453fd1cad3..4f6f79970e 100644
--- a/src/core/task/__tests__/flushPendingToolResultsToHistory.spec.ts
+++ b/src/core/task/__tests__/flushPendingToolResultsToHistory.spec.ts
@@ -210,6 +210,7 @@ describe("flushPendingToolResultsToHistory", () => {
mockProvider.postMessageToWebview = vi.fn().mockResolvedValue(undefined)
mockProvider.postStateToWebview = vi.fn().mockResolvedValue(undefined)
+ mockProvider.postStateToWebviewWithoutTaskHistory = vi.fn().mockResolvedValue(undefined)
mockProvider.updateTaskHistory = vi.fn().mockResolvedValue(undefined)
})
diff --git a/src/core/task/__tests__/grace-retry-errors.spec.ts b/src/core/task/__tests__/grace-retry-errors.spec.ts
index 5ea0e1ddb3..3c3e40b98c 100644
--- a/src/core/task/__tests__/grace-retry-errors.spec.ts
+++ b/src/core/task/__tests__/grace-retry-errors.spec.ts
@@ -206,6 +206,7 @@ describe("Grace Retry Error Handling", () => {
mockProvider.postMessageToWebview = vi.fn().mockResolvedValue(undefined)
mockProvider.postStateToWebview = vi.fn().mockResolvedValue(undefined)
+ mockProvider.postStateToWebviewWithoutTaskHistory = vi.fn().mockResolvedValue(undefined)
mockProvider.getState = vi.fn().mockResolvedValue({})
})
diff --git a/src/core/task/__tests__/grounding-sources.test.ts b/src/core/task/__tests__/grounding-sources.test.ts
index a33e4fd5d2..dc1212ead5 100644
--- a/src/core/task/__tests__/grounding-sources.test.ts
+++ b/src/core/task/__tests__/grounding-sources.test.ts
@@ -166,6 +166,7 @@ describe("Task grounding sources handling", () => {
// Mock provider with necessary methods
mockProvider = {
postStateToWebview: vi.fn().mockResolvedValue(undefined),
+ postStateToWebviewWithoutTaskHistory: vi.fn().mockResolvedValue(undefined),
getState: vi.fn().mockResolvedValue({
mode: "code",
experiments: {},
diff --git a/src/core/task/__tests__/native-tools-filtering.spec.ts b/src/core/task/__tests__/native-tools-filtering.spec.ts
index 761fe6e1ec..c9cd6a3060 100644
--- a/src/core/task/__tests__/native-tools-filtering.spec.ts
+++ b/src/core/task/__tests__/native-tools-filtering.spec.ts
@@ -3,9 +3,8 @@ import type { ModeConfig } from "@roo-code/types"
describe("Native Tools Filtering by Mode", () => {
describe("attemptApiRequest native tool filtering", () => {
it("should filter native tools based on mode restrictions", async () => {
- // This test verifies that when using native protocol, tools are filtered
- // by mode restrictions before being sent to the API, similar to how
- // XML tools are filtered in the system prompt.
+ // This test verifies that native tools are filtered by mode restrictions
+ // before being sent to the API.
const architectMode: ModeConfig = {
slug: "architect",
diff --git a/src/core/task/__tests__/reasoning-preservation.test.ts b/src/core/task/__tests__/reasoning-preservation.test.ts
index 3b0f773956..45fb602f66 100644
--- a/src/core/task/__tests__/reasoning-preservation.test.ts
+++ b/src/core/task/__tests__/reasoning-preservation.test.ts
@@ -166,6 +166,7 @@ describe("Task reasoning preservation", () => {
// Mock provider with necessary methods
mockProvider = {
postStateToWebview: vi.fn().mockResolvedValue(undefined),
+ postStateToWebviewWithoutTaskHistory: vi.fn().mockResolvedValue(undefined),
getState: vi.fn().mockResolvedValue({
mode: "code",
experiments: {},
diff --git a/src/core/task/__tests__/task-tool-history.spec.ts b/src/core/task/__tests__/task-tool-history.spec.ts
index fc7f2fd131..df74393156 100644
--- a/src/core/task/__tests__/task-tool-history.spec.ts
+++ b/src/core/task/__tests__/task-tool-history.spec.ts
@@ -1,7 +1,5 @@
import { describe, it, expect, beforeEach, vi } from "vitest"
import { Anthropic } from "@anthropic-ai/sdk"
-import { TOOL_PROTOCOL } from "@roo-code/types"
-import { resolveToolProtocol } from "../../../utils/resolveToolProtocol"
describe("Task Tool History Handling", () => {
describe("resumeTaskFromHistory tool block preservation", () => {
@@ -42,21 +40,6 @@ describe("Task Tool History Handling", () => {
},
]
- // Simulate the protocol check
- const mockApiConfiguration = { apiProvider: "roo" as const }
- const mockModelInfo = { supportsNativeTools: true }
- const mockExperiments = {}
-
- const protocol = TOOL_PROTOCOL.NATIVE
-
- // Test the logic that should NOT convert tool blocks for native protocol
- const useNative = protocol === TOOL_PROTOCOL.NATIVE
-
- if (!useNative) {
- // This block should NOT execute for native protocol
- throw new Error("Should not convert tool blocks for native protocol")
- }
-
// Verify tool blocks are preserved
const assistantMessage = apiHistory[1]
const userMessage = apiHistory[2]
@@ -80,51 +63,6 @@ describe("Task Tool History Handling", () => {
]),
)
})
-
- it("should convert tool blocks to text for XML protocol", () => {
- // Mock API conversation history with tool blocks
- const apiHistory: any[] = [
- {
- role: "assistant",
- content: [
- {
- type: "tool_use",
- id: "toolu_123",
- name: "read_file",
- input: { path: "config.json" },
- },
- ],
- ts: Date.now(),
- },
- ]
-
- // Simulate XML protocol - tool blocks should be converted to text
- const protocol = "xml"
- const useNative = false // XML protocol is not native
-
- // For XML protocol, we should convert tool blocks
- if (!useNative) {
- const conversationWithoutToolBlocks = apiHistory.map((message) => {
- if (Array.isArray(message.content)) {
- const newContent = message.content.map((block: any) => {
- if (block.type === "tool_use") {
- return {
- type: "text",
- text: `\n\nconfig.json\n\n`,
- }
- }
- return block
- })
- return { ...message, content: newContent }
- }
- return message
- })
-
- // Verify tool blocks were converted to text
- expect(conversationWithoutToolBlocks[0].content[0].type).toBe("text")
- expect(conversationWithoutToolBlocks[0].content[0].text).toContain("")
- }
- })
})
describe("convertToOpenAiMessages format", () => {
diff --git a/src/core/task/__tests__/task-xml-protocol-regression.spec.ts b/src/core/task/__tests__/task-xml-protocol-regression.spec.ts
deleted file mode 100644
index fe39dab1c7..0000000000
--- a/src/core/task/__tests__/task-xml-protocol-regression.spec.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { describe, it, expect } from "vitest"
-import { formatToolInvocation } from "../../tools/helpers/toolResultFormatting"
-
-/**
- * Regression tests to ensure XML protocol behavior remains unchanged
- * after adding native protocol support.
- */
-describe("XML Protocol Regression Tests", () => {
- it("should format tool invocations as XML tags for xml protocol", () => {
- const result = formatToolInvocation(
- "read_file",
- { path: "config.json", start_line: "1", end_line: "10" },
- "xml",
- )
-
- expect(result).toContain("")
- expect(result).toContain("")
- expect(result).toContain("config.json")
- expect(result).toContain("")
- expect(result).toContain("")
- expect(result).toContain("1")
- expect(result).toContain("")
- expect(result).toContain("")
- })
-
- it("should handle complex nested structures in XML format", () => {
- const result = formatToolInvocation(
- "execute_command",
- {
- command: "npm install",
- cwd: "/home/user/project",
- },
- "xml",
- )
-
- expect(result).toContain("")
- expect(result).toContain("")
- expect(result).toContain("npm install")
- expect(result).toContain("")
- expect(result).toContain("")
- expect(result).toContain("/home/user/project")
- expect(result).toContain("")
- expect(result).toContain("")
- })
-
- it("should handle empty parameters correctly in XML format", () => {
- const result = formatToolInvocation("list_files", {}, "xml")
-
- expect(result).toBe("\n\n")
- })
-
- it("should preserve XML format for tool results in conversation history", () => {
- // Simulate what happens in resumeTaskFromHistory for XML protocol
- const useNative = false // XML protocol
-
- const mockToolUse = {
- type: "tool_use",
- id: "toolu_123",
- name: "read_file",
- input: { path: "test.ts" },
- }
-
- if (!useNative) {
- // This is the conversion logic that should happen for XML
- const converted = {
- type: "text",
- text: formatToolInvocation(mockToolUse.name, mockToolUse.input as Record, "xml"),
- }
-
- expect(converted.type).toBe("text")
- expect(converted.text).toContain("")
- expect(converted.text).toContain("")
- expect(converted.text).toContain("test.ts")
- } else {
- throw new Error("Should not reach here for XML protocol")
- }
- })
-})
diff --git a/src/core/tools/ApplyDiffTool.ts b/src/core/tools/ApplyDiffTool.ts
index c8024c7500..5ca7002ff2 100644
--- a/src/core/tools/ApplyDiffTool.ts
+++ b/src/core/tools/ApplyDiffTool.ts
@@ -24,15 +24,8 @@ interface ApplyDiffParams {
export class ApplyDiffTool extends BaseTool<"apply_diff"> {
readonly name = "apply_diff" as const
- parseLegacy(params: Partial>): ApplyDiffParams {
- return {
- path: params.path || "",
- diff: params.diff || "",
- }
- }
-
async execute(params: ApplyDiffParams, task: Task, callbacks: ToolCallbacks): Promise {
- const { askApproval, handleError, pushToolResult, toolProtocol } = callbacks
+ const { askApproval, handleError, pushToolResult } = callbacks
let { path: relPath, diff: diffContent } = params
if (diffContent && !task.api.getModel().id.includes("claude")) {
@@ -58,7 +51,7 @@ export class ApplyDiffTool extends BaseTool<"apply_diff"> {
if (!accessAllowed) {
await task.say("rooignore_error", relPath)
- pushToolResult(formatResponse.rooIgnoreError(relPath, toolProtocol))
+ pushToolResult(formatResponse.rooIgnoreError(relPath))
return
}
@@ -239,7 +232,7 @@ export class ApplyDiffTool extends BaseTool<"apply_diff"> {
let partFailHint = ""
if (diffResult.failParts && diffResult.failParts.length > 0) {
- partFailHint = `But unable to apply all diff parts to file: ${absolutePath}, silently use tool to check newest file version and re-apply diffs\n`
+ partFailHint = `But unable to apply all diff parts to file: ${absolutePath}. Use the read_file tool to check the newest file version and re-apply diffs.\n`
}
// Get the formatted response message
diff --git a/src/core/tools/ApplyPatchTool.ts b/src/core/tools/ApplyPatchTool.ts
index bf4cdaa1b8..0c3a1765f2 100644
--- a/src/core/tools/ApplyPatchTool.ts
+++ b/src/core/tools/ApplyPatchTool.ts
@@ -23,15 +23,9 @@ interface ApplyPatchParams {
export class ApplyPatchTool extends BaseTool<"apply_patch"> {
readonly name = "apply_patch" as const
- parseLegacy(params: Partial>): ApplyPatchParams {
- return {
- patch: params.patch || "",
- }
- }
-
async execute(params: ApplyPatchParams, task: Task, callbacks: ToolCallbacks): Promise {
const { patch } = params
- const { askApproval, handleError, pushToolResult, toolProtocol } = callbacks
+ const { askApproval, handleError, pushToolResult } = callbacks
try {
// Validate required parameters
@@ -88,7 +82,7 @@ export class ApplyPatchTool extends BaseTool<"apply_patch"> {
const accessAllowed = task.rooIgnoreController?.validateAccess(relPath)
if (!accessAllowed) {
await task.say("rooignore_error", relPath)
- pushToolResult(formatResponse.rooIgnoreError(relPath, toolProtocol))
+ pushToolResult(formatResponse.rooIgnoreError(relPath))
return
}
diff --git a/src/core/tools/AskFollowupQuestionTool.ts b/src/core/tools/AskFollowupQuestionTool.ts
index 69146a4c2e..010a6240f1 100644
--- a/src/core/tools/AskFollowupQuestionTool.ts
+++ b/src/core/tools/AskFollowupQuestionTool.ts
@@ -1,6 +1,5 @@
import { Task } from "../task/Task"
import { formatResponse } from "../prompts/responses"
-import { parseXml } from "../../utils/xml"
import type { ToolUse } from "../../shared/tools"
import { BaseTool, ToolCallbacks } from "./BaseTool"
@@ -18,55 +17,9 @@ interface AskFollowupQuestionParams {
export class AskFollowupQuestionTool extends BaseTool<"ask_followup_question"> {
readonly name = "ask_followup_question" as const
- parseLegacy(params: Partial>): AskFollowupQuestionParams {
- const question = params.question || ""
- const follow_up_xml = params.follow_up
-
- const suggestions: Suggestion[] = []
-
- if (follow_up_xml) {
- // Define the actual structure returned by the XML parser
- type ParsedSuggestion = string | { "#text": string; "@_mode"?: string }
-
- try {
- const parsedSuggest = parseXml(follow_up_xml, ["suggest"]) as {
- suggest: ParsedSuggestion[] | ParsedSuggestion
- }
-
- const rawSuggestions = Array.isArray(parsedSuggest?.suggest)
- ? parsedSuggest.suggest
- : [parsedSuggest?.suggest].filter((sug): sug is ParsedSuggestion => sug !== undefined)
-
- // Transform parsed XML to our Suggest format
- for (const sug of rawSuggestions) {
- if (typeof sug === "string") {
- // Simple string suggestion (no mode attribute)
- suggestions.push({ text: sug })
- } else {
- // XML object with text content and optional mode attribute
- const suggestion: Suggestion = { text: sug["#text"] }
- if (sug["@_mode"]) {
- suggestion.mode = sug["@_mode"]
- }
- suggestions.push(suggestion)
- }
- }
- } catch (error) {
- throw new Error(
- `Failed to parse follow_up XML: ${error instanceof Error ? error.message : String(error)}`,
- )
- }
- }
-
- return {
- question,
- follow_up: suggestions,
- }
- }
-
async execute(params: AskFollowupQuestionParams, task: Task, callbacks: ToolCallbacks): Promise {
const { question, follow_up } = params
- const { handleError, pushToolResult, toolProtocol } = callbacks
+ const { handleError, pushToolResult } = callbacks
try {
if (!question) {
@@ -93,14 +46,11 @@ export class AskFollowupQuestionTool extends BaseTool<"ask_followup_question"> {
}
override async handlePartial(task: Task, block: ToolUse<"ask_followup_question">): Promise {
- // Get question from params (for XML protocol) or nativeArgs (for native protocol)
- const question: string | undefined = block.params.question ?? block.nativeArgs?.question
+ const question: string | undefined = block.nativeArgs?.question ?? block.params.question
// During partial streaming, only show the question to avoid displaying raw JSON
// The full JSON with suggestions will be sent when the tool call is complete (!block.partial)
- await task
- .ask("followup", this.removeClosingTag("question", question, block.partial), block.partial)
- .catch(() => {})
+ await task.ask("followup", question ?? "", block.partial).catch(() => {})
}
}
diff --git a/src/core/tools/AttemptCompletionTool.ts b/src/core/tools/AttemptCompletionTool.ts
index 7e8e781628..a406a15c8b 100644
--- a/src/core/tools/AttemptCompletionTool.ts
+++ b/src/core/tools/AttemptCompletionTool.ts
@@ -36,13 +36,6 @@ interface DelegationProvider {
export class AttemptCompletionTool extends BaseTool<"attempt_completion"> {
readonly name = "attempt_completion" as const
- parseLegacy(params: Partial>): AttemptCompletionParams {
- return {
- result: params.result || "",
- command: params.command,
- }
- }
-
async execute(params: AttemptCompletionParams, task: Task, callbacks: AttemptCompletionCallbacks): Promise {
const { result } = params
const { handleError, pushToolResult, askFinishSubTaskApproval } = callbacks
@@ -194,16 +187,9 @@ export class AttemptCompletionTool extends BaseTool<"attempt_completion"> {
if (command) {
if (lastMessage && lastMessage.ask === "command") {
- await task
- .ask("command", this.removeClosingTag("command", command, block.partial), block.partial)
- .catch(() => {})
+ await task.ask("command", command ?? "", block.partial).catch(() => {})
} else {
- await task.say(
- "completion_result",
- this.removeClosingTag("result", result, block.partial),
- undefined,
- false,
- )
+ await task.say("completion_result", result ?? "", undefined, false)
// Force final token usage update before emitting TaskCompleted for consistency
task.emitFinalTokenUsageUpdate()
@@ -211,17 +197,10 @@ export class AttemptCompletionTool extends BaseTool<"attempt_completion"> {
TelemetryService.instance.captureTaskCompleted(task.taskId)
task.emit(RooCodeEventName.TaskCompleted, task.taskId, task.getTokenUsage(), task.toolUsage)
- await task
- .ask("command", this.removeClosingTag("command", command, block.partial), block.partial)
- .catch(() => {})
+ await task.ask("command", command ?? "", block.partial).catch(() => {})
}
} else {
- await task.say(
- "completion_result",
- this.removeClosingTag("result", result, block.partial),
- undefined,
- block.partial,
- )
+ await task.say("completion_result", result ?? "", undefined, block.partial)
}
}
}
diff --git a/src/core/tools/BaseTool.ts b/src/core/tools/BaseTool.ts
index e18c3593e4..7d574068a9 100644
--- a/src/core/tools/BaseTool.ts
+++ b/src/core/tools/BaseTool.ts
@@ -1,14 +1,7 @@
-import type { ToolName, ToolProtocol } from "@roo-code/types"
+import type { ToolName } from "@roo-code/types"
import { Task } from "../task/Task"
-import type {
- ToolUse,
- HandleError,
- PushToolResult,
- RemoveClosingTag,
- AskApproval,
- NativeToolArgs,
-} from "../../shared/tools"
+import type { ToolUse, HandleError, PushToolResult, AskApproval, NativeToolArgs } from "../../shared/tools"
/**
* Callbacks passed to tool execution
@@ -17,8 +10,6 @@ export interface ToolCallbacks {
askApproval: AskApproval
handleError: HandleError
pushToolResult: PushToolResult
- removeClosingTag: RemoveClosingTag
- toolProtocol: ToolProtocol
toolCallId?: string
}
@@ -31,14 +22,7 @@ type ToolParams = TName extends keyof NativeToolArgs ? N
/**
* Abstract base class for all tools.
*
- * Provides a consistent architecture where:
- * - XML/legacy protocol: params → parseLegacy() → typed params → execute()
- * - Native protocol: nativeArgs already contain typed data → execute()
- *
- * Each tool extends this class and implements:
- * - parseLegacy(): Convert XML/legacy string params to typed params
- * - execute(): Protocol-agnostic core logic using typed params
- * - handlePartial(): (optional) Handle streaming partial messages
+ * Tools receive typed arguments from native tool calling via `ToolUse.nativeArgs`.
*
* @template TName - The specific tool name, which determines native arg types
*/
@@ -54,24 +38,10 @@ export abstract class BaseTool {
*/
protected lastSeenPartialPath: string | undefined = undefined
- /**
- * Parse XML/legacy string-based parameters into typed parameters.
- *
- * For XML protocol, this converts params.args (XML string) or params.path (legacy)
- * into a typed structure that execute() can use.
- *
- * @param params - Raw ToolUse.params from XML protocol
- * @returns Typed parameters for execute()
- * @throws Error if parsing fails
- */
- abstract parseLegacy(params: Partial>): ToolParams
-
/**
* Execute the tool with typed parameters.
*
- * This is the protocol-agnostic core logic. It receives typed parameters
- * (from parseLegacy for XML, or directly from native protocol) and performs
- * the tool's operation.
+ * Receives typed parameters from native tool calling via `ToolUse.nativeArgs`.
*
* @param params - Typed parameters
* @param task - Task instance with state and API access
@@ -93,40 +63,6 @@ export abstract class BaseTool {
// Tools can override to show streaming UI updates
}
- /**
- * Remove partial closing XML tags from text during streaming.
- *
- * This utility helps clean up partial XML tag artifacts that can appear
- * at the end of streamed content, preventing them from being displayed to users.
- *
- * @param tag - The tag name to check for partial closing
- * @param text - The text content to clean
- * @param isPartial - Whether this is a partial message (if false, returns text as-is)
- * @returns Cleaned text with partial closing tags removed
- */
- protected removeClosingTag(tag: string, text: string | undefined, isPartial: boolean): string {
- if (!isPartial) {
- return text || ""
- }
-
- if (!text) {
- return ""
- }
-
- // This regex dynamically constructs a pattern to match the closing tag:
- // - Optionally matches whitespace before the tag
- // - Matches '<' or '' optionally followed by any subset of characters from the tag name
- const tagRegex = new RegExp(
- `\\s?<\/?${tag
- .split("")
- .map((char) => `(?:${char})?`)
- .join("")}$`,
- "g",
- )
-
- return text.replace(tagRegex, "")
- }
-
/**
* Check if a path parameter has stabilized during streaming.
*
@@ -167,7 +103,7 @@ export abstract class BaseTool {
*
* Handles the complete flow:
* 1. Partial message handling (if partial)
- * 2. Parameter parsing (parseLegacy for XML, or use nativeArgs directly)
+ * 2. Parameter parsing (nativeArgs only)
* 3. Core execution (execute)
*
* @param task - Task instance
@@ -189,22 +125,34 @@ export abstract class BaseTool {
return
}
- // Determine protocol and parse parameters accordingly
+ // Native-only: obtain typed parameters from `nativeArgs`.
let params: ToolParams
try {
if (block.nativeArgs !== undefined) {
- // Native protocol: typed args provided by NativeToolCallParser
- // TypeScript knows nativeArgs is properly typed based on TName
+ // Native: typed args provided by NativeToolCallParser.
params = block.nativeArgs as ToolParams
} else {
- // XML/legacy protocol: parse string params into typed params
- params = this.parseLegacy(block.params)
+ // If legacy/XML markup was provided via params, surface a clear error.
+ const paramsText = (() => {
+ try {
+ return JSON.stringify(block.params ?? {})
+ } catch {
+ return ""
+ }
+ })()
+ if (paramsText.includes("<") && paramsText.includes(">")) {
+ throw new Error(
+ "XML tool calls are no longer supported. Use native tool calling (nativeArgs) instead.",
+ )
+ }
+ throw new Error("Tool call is missing native arguments (nativeArgs).")
}
} catch (error) {
console.error(`Error parsing parameters:`, error)
const errorMessage = `Failed to parse ${this.name} parameters: ${error instanceof Error ? error.message : String(error)}`
await callbacks.handleError(`parsing ${this.name} args`, new Error(errorMessage))
- callbacks.pushToolResult(`${errorMessage}`)
+ // Note: handleError already emits a tool_result via formatResponse.toolError in the caller.
+ // Do NOT call pushToolResult here to avoid duplicate tool_result payloads.
return
}
diff --git a/src/core/tools/BrowserActionTool.ts b/src/core/tools/BrowserActionTool.ts
index 39a2bab3d1..3bd584e0cb 100644
--- a/src/core/tools/BrowserActionTool.ts
+++ b/src/core/tools/BrowserActionTool.ts
@@ -3,7 +3,7 @@ import { Anthropic } from "@anthropic-ai/sdk"
import { BrowserAction, BrowserActionResult, browserActions, ClineSayBrowserAction } from "@roo-code/types"
import { Task } from "../task/Task"
-import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
+import { ToolUse, AskApproval, HandleError, PushToolResult } from "../../shared/tools"
import { formatResponse } from "../prompts/responses"
import { scaleCoordinate } from "../../shared/browserUtils"
@@ -14,7 +14,6 @@ export async function browserActionTool(
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
- removeClosingTag: RemoveClosingTag,
) {
const action: BrowserAction | undefined = block.params.action as BrowserAction
const url: string | undefined = block.params.url
@@ -40,15 +39,15 @@ export async function browserActionTool(
try {
if (block.partial) {
if (action === "launch") {
- await cline.ask("browser_action_launch", removeClosingTag("url", url), block.partial).catch(() => {})
+ await cline.ask("browser_action_launch", url ?? "", block.partial).catch(() => {})
} else {
await cline.say(
"browser_action",
JSON.stringify({
action: action as BrowserAction,
- coordinate: removeClosingTag("coordinate", coordinate),
- text: removeClosingTag("text", text),
- size: removeClosingTag("size", size),
+ coordinate: coordinate ?? "",
+ text: text ?? "",
+ size: size ?? "",
} satisfies ClineSayBrowserAction),
undefined,
block.partial,
diff --git a/src/core/tools/CodebaseSearchTool.ts b/src/core/tools/CodebaseSearchTool.ts
index 96b5cb5d08..f0d906fabd 100644
--- a/src/core/tools/CodebaseSearchTool.ts
+++ b/src/core/tools/CodebaseSearchTool.ts
@@ -18,22 +18,8 @@ interface CodebaseSearchParams {
export class CodebaseSearchTool extends BaseTool<"codebase_search"> {
readonly name = "codebase_search" as const
- parseLegacy(params: Partial>): CodebaseSearchParams {
- let query = params.query
- let directoryPrefix = params.path
-
- if (directoryPrefix) {
- directoryPrefix = path.normalize(directoryPrefix)
- }
-
- return {
- query: query || "",
- path: directoryPrefix,
- }
- }
-
async execute(params: CodebaseSearchParams, task: Task, callbacks: ToolCallbacks): Promise {
- const { askApproval, handleError, pushToolResult, toolProtocol } = callbacks
+ const { askApproval, handleError, pushToolResult } = callbacks
const { query, path: directoryPrefix } = params
const workspacePath = task.cwd && task.cwd.trim() !== "" ? task.cwd : getWorkspacePath()
diff --git a/src/core/tools/EditFileTool.ts b/src/core/tools/EditFileTool.ts
index f2369c76f3..2495a372bc 100644
--- a/src/core/tools/EditFileTool.ts
+++ b/src/core/tools/EditFileTool.ts
@@ -136,17 +136,6 @@ export class EditFileTool extends BaseTool<"edit_file"> {
private didSendPartialToolAsk = false
private partialToolAskRelPath: string | undefined
- parseLegacy(params: Partial>): EditFileParams {
- return {
- file_path: params.file_path || "",
- old_string: params.old_string || "",
- new_string: params.new_string || "",
- expected_replacements: params.expected_replacements
- ? parseInt(params.expected_replacements, 10)
- : undefined,
- }
- }
-
async execute(params: EditFileParams, task: Task, callbacks: ToolCallbacks): Promise {
// Coerce old_string/new_string to handle malformed native tool calls where they could be non-strings.
// In native mode, malformed calls can pass numbers/objects; normalize those to "" to avoid later crashes.
@@ -154,7 +143,7 @@ export class EditFileTool extends BaseTool<"edit_file"> {
const old_string = typeof params.old_string === "string" ? params.old_string : ""
const new_string = typeof params.new_string === "string" ? params.new_string : ""
const expected_replacements = params.expected_replacements ?? 1
- const { askApproval, handleError, pushToolResult, toolProtocol } = callbacks
+ const { askApproval, handleError, pushToolResult } = callbacks
let relPathForErrorHandling: string | undefined
let operationPreviewForErrorHandling: string | undefined
@@ -224,7 +213,7 @@ export class EditFileTool extends BaseTool<"edit_file"> {
await finalizePartialToolAskIfNeeded(relPath)
task.didToolFailInCurrentTurn = true
await task.say("rooignore_error", relPath)
- pushToolResult(formatResponse.rooIgnoreError(relPath, toolProtocol))
+ pushToolResult(formatResponse.rooIgnoreError(relPath))
return
}
diff --git a/src/core/tools/ExecuteCommandTool.ts b/src/core/tools/ExecuteCommandTool.ts
index 52f4743306..d3e2bbce8d 100644
--- a/src/core/tools/ExecuteCommandTool.ts
+++ b/src/core/tools/ExecuteCommandTool.ts
@@ -29,16 +29,9 @@ interface ExecuteCommandParams {
export class ExecuteCommandTool extends BaseTool<"execute_command"> {
readonly name = "execute_command" as const
- parseLegacy(params: Partial>): ExecuteCommandParams {
- return {
- command: params.command || "",
- cwd: params.cwd,
- }
- }
-
async execute(params: ExecuteCommandParams, task: Task, callbacks: ToolCallbacks): Promise {
const { command, cwd: customCwd } = params
- const { handleError, pushToolResult, askApproval, removeClosingTag, toolProtocol } = callbacks
+ const { handleError, pushToolResult, askApproval } = callbacks
try {
if (!command) {
@@ -52,7 +45,7 @@ export class ExecuteCommandTool extends BaseTool<"execute_command"> {
if (ignoredFileAttemptedToAccess) {
await task.say("rooignore_error", ignoredFileAttemptedToAccess)
- pushToolResult(formatResponse.rooIgnoreError(ignoredFileAttemptedToAccess, toolProtocol))
+ pushToolResult(formatResponse.rooIgnoreError(ignoredFileAttemptedToAccess))
return
}
@@ -144,9 +137,7 @@ export class ExecuteCommandTool extends BaseTool<"execute_command"> {
override async handlePartial(task: Task, block: ToolUse<"execute_command">): Promise {
const command = block.params.command
- await task
- .ask("command", this.removeClosingTag("command", command, block.partial), block.partial)
- .catch(() => {})
+ await task.ask("command", command ?? "", block.partial).catch(() => {})
}
}
diff --git a/src/core/tools/FetchInstructionsTool.ts b/src/core/tools/FetchInstructionsTool.ts
index 7749de2cb8..f800e57fc4 100644
--- a/src/core/tools/FetchInstructionsTool.ts
+++ b/src/core/tools/FetchInstructionsTool.ts
@@ -14,14 +14,8 @@ interface FetchInstructionsParams {
export class FetchInstructionsTool extends BaseTool<"fetch_instructions"> {
readonly name = "fetch_instructions" as const
- parseLegacy(params: Partial>): FetchInstructionsParams {
- return {
- task: params.task || "",
- }
- }
-
async execute(params: FetchInstructionsParams, task: Task, callbacks: ToolCallbacks): Promise {
- const { handleError, pushToolResult, askApproval, toolProtocol } = callbacks
+ const { handleError, pushToolResult, askApproval } = callbacks
const { task: taskParam } = params
try {
diff --git a/src/core/tools/GenerateImageTool.ts b/src/core/tools/GenerateImageTool.ts
index d4bbe980d6..3eaa2d84c2 100644
--- a/src/core/tools/GenerateImageTool.ts
+++ b/src/core/tools/GenerateImageTool.ts
@@ -22,17 +22,9 @@ import { t } from "../../i18n"
export class GenerateImageTool extends BaseTool<"generate_image"> {
readonly name = "generate_image" as const
- parseLegacy(params: Partial>): GenerateImageParams {
- return {
- prompt: params.prompt || "",
- path: params.path || "",
- image: params.image,
- }
- }
-
async execute(params: GenerateImageParams, task: Task, callbacks: ToolCallbacks): Promise {
const { prompt, path: relPath, image: inputImagePath } = params
- const { handleError, pushToolResult, askApproval, removeClosingTag, toolProtocol } = callbacks
+ const { handleError, pushToolResult, askApproval } = callbacks
const provider = task.providerRef.deref()
const state = await provider?.getState()
@@ -67,7 +59,7 @@ export class GenerateImageTool extends BaseTool<"generate_image"> {
const accessAllowed = task.rooIgnoreController?.validateAccess(relPath)
if (!accessAllowed) {
await task.say("rooignore_error", relPath)
- pushToolResult(formatResponse.rooIgnoreError(relPath, toolProtocol))
+ pushToolResult(formatResponse.rooIgnoreError(relPath))
return
}
@@ -88,7 +80,7 @@ export class GenerateImageTool extends BaseTool<"generate_image"> {
const inputImageAccessAllowed = task.rooIgnoreController?.validateAccess(inputImagePath)
if (!inputImageAccessAllowed) {
await task.say("rooignore_error", inputImagePath)
- pushToolResult(formatResponse.rooIgnoreError(inputImagePath, toolProtocol))
+ pushToolResult(formatResponse.rooIgnoreError(inputImagePath))
return
}
@@ -171,12 +163,12 @@ export class GenerateImageTool extends BaseTool<"generate_image"> {
return
}
- const fullPath = path.resolve(task.cwd, removeClosingTag("path", relPath))
+ const fullPath = path.resolve(task.cwd, relPath)
const isOutsideWorkspace = isPathOutsideWorkspace(fullPath)
const sharedMessageProps = {
tool: "generateImage" as const,
- path: getReadablePath(task.cwd, removeClosingTag("path", relPath)),
+ path: getReadablePath(task.cwd, relPath),
content: prompt,
isOutsideWorkspace,
isProtected: isWriteProtected,
diff --git a/src/core/tools/ListFilesTool.ts b/src/core/tools/ListFilesTool.ts
index b4128d2a85..716d7ed784 100644
--- a/src/core/tools/ListFilesTool.ts
+++ b/src/core/tools/ListFilesTool.ts
@@ -19,19 +19,9 @@ interface ListFilesParams {
export class ListFilesTool extends BaseTool<"list_files"> {
readonly name = "list_files" as const
- parseLegacy(params: Partial>): ListFilesParams {
- const recursiveRaw: string | undefined = params.recursive
- const recursive = recursiveRaw?.toLowerCase() === "true"
-
- return {
- path: params.path || "",
- recursive,
- }
- }
-
async execute(params: ListFilesParams, task: Task, callbacks: ToolCallbacks): Promise {
const { path: relDirPath, recursive } = params
- const { askApproval, handleError, pushToolResult, removeClosingTag } = callbacks
+ const { askApproval, handleError, pushToolResult } = callbacks
try {
if (!relDirPath) {
@@ -88,7 +78,7 @@ export class ListFilesTool extends BaseTool<"list_files"> {
const sharedMessageProps: ClineSayTool = {
tool: !recursive ? "listFilesTopLevel" : "listFilesRecursive",
- path: getReadablePath(task.cwd, this.removeClosingTag("path", relDirPath, block.partial)),
+ path: getReadablePath(task.cwd, relDirPath ?? ""),
isOutsideWorkspace,
}
diff --git a/src/core/tools/MultiApplyDiffTool.ts b/src/core/tools/MultiApplyDiffTool.ts
index af5fefa251..642479b4f2 100644
--- a/src/core/tools/MultiApplyDiffTool.ts
+++ b/src/core/tools/MultiApplyDiffTool.ts
@@ -1,55 +1,6 @@
-import path from "path"
-import fs from "fs/promises"
-
-import { type ClineSayTool, DEFAULT_WRITE_DELAY_MS, isNativeProtocol } from "@roo-code/types"
-import { TelemetryService } from "@roo-code/telemetry"
-
-import { getReadablePath } from "../../utils/path"
import { Task } from "../task/Task"
-import { ToolUse, RemoveClosingTag, AskApproval, HandleError, PushToolResult } from "../../shared/tools"
-import { formatResponse } from "../prompts/responses"
-import { fileExistsAtPath } from "../../utils/fs"
-import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
-import { unescapeHtmlEntities } from "../../utils/text-normalization"
-import { parseXmlForDiff } from "../../utils/xml"
-import { EXPERIMENT_IDS, experiments } from "../../shared/experiments"
+import { ToolUse, AskApproval, HandleError, PushToolResult } from "../../shared/tools"
import { applyDiffTool as applyDiffToolClass } from "./ApplyDiffTool"
-import { computeDiffStats, sanitizeUnifiedDiff } from "../diff/stats"
-import { resolveToolProtocol } from "../../utils/resolveToolProtocol"
-
-interface DiffOperation {
- path: string
- diff: Array<{
- content: string
- startLine?: number
- }>
-}
-
-// Track operation status
-interface OperationResult {
- path: string
- status: "pending" | "approved" | "denied" | "blocked" | "error"
- error?: string
- result?: string
- diffItems?: Array<{ content: string; startLine?: number }>
- absolutePath?: string
- fileExists?: boolean
-}
-
-// Add proper type definitions
-interface ParsedFile {
- path: string
- diff: ParsedDiff | ParsedDiff[]
-}
-
-interface ParsedDiff {
- content: string
- start_line?: string
-}
-
-interface ParsedXmlResult {
- file: ParsedFile | ParsedFile[]
-}
export async function applyDiffTool(
cline: Task,
@@ -57,708 +8,10 @@ export async function applyDiffTool(
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
- removeClosingTag: RemoveClosingTag,
) {
- // Check if native protocol is enabled - if so, always use single-file class-based tool
- // Use the task's locked protocol for consistency throughout the task lifetime
- const toolProtocol = resolveToolProtocol(cline.apiConfiguration, cline.api.getModel().info, cline.taskToolProtocol)
- if (isNativeProtocol(toolProtocol)) {
- return applyDiffToolClass.handle(cline, block as ToolUse<"apply_diff">, {
- askApproval,
- handleError,
- pushToolResult,
- removeClosingTag,
- toolProtocol,
- })
- }
-
- // Check if MULTI_FILE_APPLY_DIFF experiment is enabled
- const provider = cline.providerRef.deref()
- const state = await provider?.getState()
- if (provider && state) {
- const isMultiFileApplyDiffEnabled = experiments.isEnabled(
- state.experiments ?? {},
- EXPERIMENT_IDS.MULTI_FILE_APPLY_DIFF,
- )
-
- // If experiment is disabled, use single-file class-based tool
- if (!isMultiFileApplyDiffEnabled) {
- return applyDiffToolClass.handle(cline, block as ToolUse<"apply_diff">, {
- askApproval,
- handleError,
- pushToolResult,
- removeClosingTag,
- toolProtocol,
- })
- }
- }
-
- // Otherwise, continue with new multi-file implementation
- const argsXmlTag: string | undefined = block.params.args
- const legacyPath: string | undefined = block.params.path
- const legacyDiffContent: string | undefined = block.params.diff
- const legacyStartLineStr: string | undefined = block.params.start_line
-
- let operationsMap: Record = {}
- let usingLegacyParams = false
- let filteredOperationErrors: string[] = []
-
- // Handle partial message first
- if (block.partial) {
- let filePath = ""
- if (argsXmlTag) {
- const match = argsXmlTag.match(/.*?([^<]+)<\/path>/s)
- if (match) {
- filePath = match[1]
- }
- } else if (legacyPath) {
- // Use legacy path if argsXmlTag is not present for partial messages
- filePath = legacyPath
- }
-
- const sharedMessageProps: ClineSayTool = {
- tool: "appliedDiff",
- path: getReadablePath(cline.cwd, filePath),
- }
- const partialMessage = JSON.stringify(sharedMessageProps)
- await cline.ask("tool", partialMessage, block.partial).catch(() => {})
- return
- }
-
- if (argsXmlTag) {
- // Parse file entries from XML (new way)
- try {
- // IMPORTANT: We use parseXmlForDiff here instead of parseXml to prevent HTML entity decoding
- // This ensures exact character matching when comparing parsed content against original file content
- // Without this, special characters like & would be decoded to & causing diff mismatches
- const parsed = parseXmlForDiff(argsXmlTag, ["file.diff.content"]) as ParsedXmlResult
- const files = Array.isArray(parsed.file) ? parsed.file : [parsed.file].filter(Boolean)
-
- for (const file of files) {
- if (!file.path || !file.diff) continue
-
- const filePath = file.path
-
- // Initialize the operation in the map if it doesn't exist
- if (!operationsMap[filePath]) {
- operationsMap[filePath] = {
- path: filePath,
- diff: [],
- }
- }
-
- // Handle diff as either array or single element
- const diffs = Array.isArray(file.diff) ? file.diff : [file.diff]
-
- for (let i = 0; i < diffs.length; i++) {
- const diff = diffs[i]
- let diffContent: string
- let startLine: number | undefined
-
- // Ensure content is a string before storing it
- diffContent = typeof diff.content === "string" ? diff.content : ""
- startLine = diff.start_line ? parseInt(diff.start_line) : undefined
-
- // Only add to operations if we have valid content
- if (diffContent) {
- operationsMap[filePath].diff.push({
- content: diffContent,
- startLine,
- })
- }
- }
- }
- } catch (error) {
- const errorMessage = error instanceof Error ? error.message : String(error)
- const detailedError = `Failed to parse apply_diff XML. This usually means:
-1. The XML structure is malformed or incomplete
-2. Missing required , , or tags
-3. Invalid characters or encoding in the XML
-
-Expected structure:
-
-
- relative/path/to/file.ext
-
- diff content here
- line number
-
-
-
-
-Original error: ${errorMessage}`
- cline.consecutiveMistakeCount++
- cline.recordToolError("apply_diff")
- TelemetryService.instance.captureDiffApplicationError(cline.taskId, cline.consecutiveMistakeCount)
- await cline.say("diff_error", `Failed to parse apply_diff XML: ${errorMessage}`)
- pushToolResult(detailedError)
- cline.processQueuedMessages()
- return
- }
- } else if (legacyPath && typeof legacyDiffContent === "string") {
- // Handle legacy parameters (old way)
- usingLegacyParams = true
- operationsMap[legacyPath] = {
- path: legacyPath,
- diff: [
- {
- content: legacyDiffContent, // Unescaping will be handled later like new diffs
- startLine: legacyStartLineStr ? parseInt(legacyStartLineStr) : undefined,
- },
- ],
- }
- } else {
- // Neither new XML args nor old path/diff params are sufficient
- cline.consecutiveMistakeCount++
- cline.recordToolError("apply_diff")
- const errorMsg = await cline.sayAndCreateMissingParamError(
- "apply_diff",
- "args (or legacy 'path' and 'diff' parameters)",
- )
- pushToolResult(errorMsg)
- cline.processQueuedMessages()
- return
- }
-
- // If no operations were extracted, bail out
- if (Object.keys(operationsMap).length === 0) {
- cline.consecutiveMistakeCount++
- cline.recordToolError("apply_diff")
- pushToolResult(
- await cline.sayAndCreateMissingParamError(
- "apply_diff",
- usingLegacyParams
- ? "legacy 'path' and 'diff' (must be valid and non-empty)"
- : "args (must contain at least one valid file element)",
- ),
- )
- cline.processQueuedMessages()
- return
- }
-
- // Convert map to array of operations for processing
- const operations = Object.values(operationsMap)
-
- const operationResults: OperationResult[] = operations.map((op) => ({
- path: op.path,
- status: "pending",
- diffItems: op.diff,
- }))
-
- // Function to update operation result
- const updateOperationResult = (path: string, updates: Partial) => {
- const index = operationResults.findIndex((result) => result.path === path)
- if (index !== -1) {
- operationResults[index] = { ...operationResults[index], ...updates }
- }
- }
-
- try {
- // First validate all files and prepare for batch approval
- const operationsToApprove: OperationResult[] = []
- const allDiffErrors: string[] = [] // Collect all diff errors
-
- for (const operation of operations) {
- const { path: relPath, diff: diffItems } = operation
-
- // Verify file access is allowed
- const accessAllowed = cline.rooIgnoreController?.validateAccess(relPath)
- if (!accessAllowed) {
- await cline.say("rooignore_error", relPath)
- updateOperationResult(relPath, {
- status: "blocked",
- error: formatResponse.rooIgnoreError(relPath, undefined),
- })
- continue
- }
-
- // Check if file is write-protected
- const isWriteProtected = cline.rooProtectedController?.isWriteProtected(relPath) || false
-
- // Verify file exists
- const absolutePath = path.resolve(cline.cwd, relPath)
- const fileExists = await fileExistsAtPath(absolutePath)
- if (!fileExists) {
- updateOperationResult(relPath, {
- status: "blocked",
- error: `File does not exist at path: ${absolutePath}`,
- })
- continue
- }
-
- // Add to operations that need approval
- const opResult = operationResults.find((r) => r.path === relPath)
- if (opResult) {
- opResult.absolutePath = absolutePath
- opResult.fileExists = fileExists
- operationsToApprove.push(opResult)
- }
- }
-
- // Handle batch approval if there are multiple files
- if (operationsToApprove.length > 1) {
- // Check if any files are write-protected
- const hasProtectedFiles = operationsToApprove.some(
- (opResult) => cline.rooProtectedController?.isWriteProtected(opResult.path) || false,
- )
-
- // Stream batch diffs progressively for better UX
- const batchDiffs: Array<{
- path: string
- changeCount: number
- key: string
- content: string
- diffStats?: { added: number; removed: number }
- diffs?: Array<{ content: string; startLine?: number }>
- }> = []
-
- for (const opResult of operationsToApprove) {
- const readablePath = getReadablePath(cline.cwd, opResult.path)
- const changeCount = opResult.diffItems?.length || 0
- const changeText = changeCount === 1 ? "1 change" : `${changeCount} changes`
-
- let unified = ""
- try {
- const original = await fs.readFile(opResult.absolutePath!, "utf-8")
- const processed = !cline.api.getModel().id.includes("claude")
- ? (opResult.diffItems || []).map((item) => ({
- ...item,
- content: item.content ? unescapeHtmlEntities(item.content) : item.content,
- }))
- : opResult.diffItems || []
-
- const applyRes =
- (await cline.diffStrategy?.applyDiff(original, processed)) ?? ({ success: false } as any)
- const newContent = applyRes.success && applyRes.content ? applyRes.content : original
- unified = formatResponse.createPrettyPatch(opResult.path, original, newContent)
- } catch {
- unified = ""
- }
-
- const unifiedSanitized = sanitizeUnifiedDiff(unified)
- const stats = computeDiffStats(unifiedSanitized) || undefined
- batchDiffs.push({
- path: readablePath,
- changeCount,
- key: `${readablePath} (${changeText})`,
- content: unifiedSanitized,
- diffStats: stats,
- diffs: opResult.diffItems?.map((item) => ({
- content: item.content,
- startLine: item.startLine,
- })),
- })
-
- // Send a partial update after each file preview is ready
- const partialMessage = JSON.stringify({
- tool: "appliedDiff",
- batchDiffs,
- isProtected: hasProtectedFiles,
- } satisfies ClineSayTool)
- await cline.ask("tool", partialMessage, true).catch(() => {})
- }
-
- // Final approval message (non-partial)
- const completeMessage = JSON.stringify({
- tool: "appliedDiff",
- batchDiffs,
- isProtected: hasProtectedFiles,
- } satisfies ClineSayTool)
-
- const { response, text, images } = await cline.ask("tool", completeMessage, false)
-
- // Process batch response
- if (response === "yesButtonClicked") {
- // Approve all files
- if (text) {
- await cline.say("user_feedback", text, images)
- }
- operationsToApprove.forEach((opResult) => {
- updateOperationResult(opResult.path, { status: "approved" })
- })
- } else if (response === "noButtonClicked") {
- // Deny all files
- if (text) {
- await cline.say("user_feedback", text, images)
- }
- cline.didRejectTool = true
- operationsToApprove.forEach((opResult) => {
- updateOperationResult(opResult.path, {
- status: "denied",
- result: `Changes to ${opResult.path} were not approved by user`,
- })
- })
- } else {
- // Handle individual permissions from objectResponse
- try {
- const parsedResponse = JSON.parse(text || "{}")
- // Check if this is our batch diff approval response
- if (parsedResponse.action === "applyDiff" && parsedResponse.approvedFiles) {
- const approvedFiles = parsedResponse.approvedFiles
- let hasAnyDenial = false
-
- operationsToApprove.forEach((opResult) => {
- const approved = approvedFiles[opResult.path] === true
-
- if (approved) {
- updateOperationResult(opResult.path, { status: "approved" })
- } else {
- hasAnyDenial = true
- updateOperationResult(opResult.path, {
- status: "denied",
- result: `Changes to ${opResult.path} were not approved by user`,
- })
- }
- })
-
- if (hasAnyDenial) {
- cline.didRejectTool = true
- }
- } else {
- // Legacy individual permissions format
- const individualPermissions = parsedResponse
- let hasAnyDenial = false
-
- batchDiffs.forEach((batchDiff, index) => {
- const opResult = operationsToApprove[index]
- const approved = individualPermissions[batchDiff.key] === true
-
- if (approved) {
- updateOperationResult(opResult.path, { status: "approved" })
- } else {
- hasAnyDenial = true
- updateOperationResult(opResult.path, {
- status: "denied",
- result: `Changes to ${opResult.path} were not approved by user`,
- })
- }
- })
-
- if (hasAnyDenial) {
- cline.didRejectTool = true
- }
- }
- } catch (error) {
- // Fallback: if JSON parsing fails, deny all files
- console.error("Failed to parse individual permissions:", error)
- cline.didRejectTool = true
- operationsToApprove.forEach((opResult) => {
- updateOperationResult(opResult.path, {
- status: "denied",
- result: `Changes to ${opResult.path} were not approved by user`,
- })
- })
- }
- }
- } else if (operationsToApprove.length === 1) {
- // Single file approval - process immediately
- const opResult = operationsToApprove[0]
- updateOperationResult(opResult.path, { status: "approved" })
- }
-
- // Process approved operations
- const results: string[] = []
-
- for (const opResult of operationResults) {
- // Skip operations that weren't approved or were blocked
- if (opResult.status !== "approved") {
- if (opResult.result) {
- results.push(opResult.result)
- } else if (opResult.error) {
- results.push(opResult.error)
- }
- continue
- }
-
- const relPath = opResult.path
- const diffItems = opResult.diffItems || []
- const absolutePath = opResult.absolutePath!
- const fileExists = opResult.fileExists!
-
- try {
- let originalContent: string | null = await fs.readFile(absolutePath, "utf-8")
- let beforeContent: string | null = originalContent
- let successCount = 0
- let formattedError = ""
-
- // Pre-process all diff items for HTML entity unescaping if needed
- const processedDiffItems = !cline.api.getModel().id.includes("claude")
- ? diffItems.map((item) => ({
- ...item,
- content: item.content ? unescapeHtmlEntities(item.content) : item.content,
- }))
- : diffItems
-
- // Apply all diffs at once with the array-based method
- const diffResult = (await cline.diffStrategy?.applyDiff(originalContent, processedDiffItems)) ?? {
- success: false,
- error: "No diff strategy available - please ensure a valid diff strategy is configured",
- }
-
- // Release the original content from memory as it's no longer needed
- originalContent = null
-
- if (!diffResult.success) {
- cline.consecutiveMistakeCount++
- const currentCount = (cline.consecutiveMistakeCountForApplyDiff.get(relPath) || 0) + 1
- cline.consecutiveMistakeCountForApplyDiff.set(relPath, currentCount)
-
- TelemetryService.instance.captureDiffApplicationError(cline.taskId, currentCount)
-
- if (diffResult.failParts && diffResult.failParts.length > 0) {
- for (let i = 0; i < diffResult.failParts.length; i++) {
- const failPart = diffResult.failParts[i]
- if (failPart.success) {
- continue
- }
-
- // Collect error for later reporting
- allDiffErrors.push(`${relPath} - Diff ${i + 1}: ${failPart.error}`)
-
- const errorDetails = failPart.details ? JSON.stringify(failPart.details, null, 2) : ""
- formattedError += `
-Diff ${i + 1} failed for file: ${relPath}
-Error: ${failPart.error}
-
-Suggested fixes:
-1. Verify the search content exactly matches the file content (including whitespace and case)
-2. Check for correct indentation and line endings
-3. Use the read_file tool to verify the file's current contents
-4. Consider breaking complex changes into smaller diffs
-5. Ensure start_line parameter matches the actual content location
-${errorDetails ? `\nDetailed error information:\n${errorDetails}\n` : ""}
-\n\n`
- }
- } else {
- const errorDetails = diffResult.details ? JSON.stringify(diffResult.details, null, 2) : ""
- formattedError += `
-Unable to apply diffs to file: ${absolutePath}
-Error: ${diffResult.error}
-
-Recovery suggestions:
-1. Use the read_file tool to verify the file's current contents
-2. Verify the diff format matches the expected search/replace pattern
-3. Check that the search content exactly matches what's in the file
-4. Consider using line numbers with start_line parameter
-5. Break large changes into smaller, more specific diffs
-${errorDetails ? `\nTechnical details:\n${errorDetails}\n` : ""}
-\n\n`
- }
- } else {
- // Get the content from the result and update success count
- originalContent = diffResult.content || originalContent
- successCount = diffItems.length - (diffResult.failParts?.length || 0)
- }
-
- // If no diffs were successfully applied, continue to next file
- if (successCount === 0) {
- if (formattedError) {
- const currentCount = cline.consecutiveMistakeCountForApplyDiff.get(relPath) || 0
- if (currentCount >= 2) {
- await cline.say("diff_error", formattedError)
- }
- cline.recordToolError("apply_diff", formattedError)
- results.push(formattedError)
-
- // For single file operations, we need to send a complete message to stop the spinner
- if (operationsToApprove.length === 1) {
- const sharedMessageProps: ClineSayTool = {
- tool: "appliedDiff",
- path: getReadablePath(cline.cwd, relPath),
- diff: diffItems.map((item) => item.content).join("\n\n"),
- }
- // Send a complete message (partial: false) to update the UI and stop the spinner
- await cline.ask("tool", JSON.stringify(sharedMessageProps), false).catch(() => {})
- }
- }
- continue
- }
-
- cline.consecutiveMistakeCount = 0
- cline.consecutiveMistakeCountForApplyDiff.delete(relPath)
-
- // Check if preventFocusDisruption experiment is enabled
- const provider = cline.providerRef.deref()
- const state = await provider?.getState()
- const diagnosticsEnabled = state?.diagnosticsEnabled ?? true
- const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS
- const isPreventFocusDisruptionEnabled = experiments.isEnabled(
- state?.experiments ?? {},
- EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION,
- )
-
- // For batch operations, we've already gotten approval
- const isWriteProtected = cline.rooProtectedController?.isWriteProtected(relPath) || false
- const sharedMessageProps: ClineSayTool = {
- tool: "appliedDiff",
- path: getReadablePath(cline.cwd, relPath),
- isProtected: isWriteProtected,
- }
-
- // If single file, handle based on PREVENT_FOCUS_DISRUPTION setting
- let didApprove = true
- if (operationsToApprove.length === 1) {
- // Prepare common data for single file operation
- const diffContents = diffItems.map((item) => item.content).join("\n\n")
- const unifiedPatchRaw = formatResponse.createPrettyPatch(relPath, beforeContent!, originalContent!)
- const unifiedPatch = sanitizeUnifiedDiff(unifiedPatchRaw)
- const operationMessage = JSON.stringify({
- ...sharedMessageProps,
- diff: diffContents,
- content: unifiedPatch,
- diffStats: computeDiffStats(unifiedPatch) || undefined,
- } satisfies ClineSayTool)
-
- let toolProgressStatus
- if (cline.diffStrategy && cline.diffStrategy.getProgressStatus) {
- toolProgressStatus = cline.diffStrategy.getProgressStatus(
- {
- ...block,
- params: { ...block.params, diff: diffContents },
- },
- { success: true },
- )
- }
-
- // Set up diff view
- cline.diffViewProvider.editType = "modify"
-
- // Show diff view if focus disruption prevention is disabled
- if (!isPreventFocusDisruptionEnabled) {
- await cline.diffViewProvider.open(relPath)
- await cline.diffViewProvider.update(originalContent!, true)
- cline.diffViewProvider.scrollToFirstDiff()
- } else {
- // For direct save, we still need to set originalContent
- cline.diffViewProvider.originalContent = await fs.readFile(absolutePath, "utf-8")
- }
-
- // Ask for approval (same for both flows)
- const isWriteProtected = cline.rooProtectedController?.isWriteProtected(relPath) || false
- didApprove = await askApproval("tool", operationMessage, toolProgressStatus, isWriteProtected)
-
- if (!didApprove) {
- // Revert changes if diff view was shown
- if (!isPreventFocusDisruptionEnabled) {
- await cline.diffViewProvider.revertChanges()
- }
- results.push(`Changes to ${relPath} were not approved by user`)
- continue
- }
-
- // Save the changes
- if (isPreventFocusDisruptionEnabled) {
- // Direct file write without diff view or opening the file
- await cline.diffViewProvider.saveDirectly(
- relPath,
- originalContent!,
- false,
- diagnosticsEnabled,
- writeDelayMs,
- )
- } else {
- // Call saveChanges to update the DiffViewProvider properties
- await cline.diffViewProvider.saveChanges(diagnosticsEnabled, writeDelayMs)
- }
- } else {
- // Batch operations - already approved above
- if (isPreventFocusDisruptionEnabled) {
- // Direct file write without diff view or opening the file
- cline.diffViewProvider.editType = "modify"
- cline.diffViewProvider.originalContent = await fs.readFile(absolutePath, "utf-8")
- await cline.diffViewProvider.saveDirectly(
- relPath,
- originalContent!,
- false,
- diagnosticsEnabled,
- writeDelayMs,
- )
- } else {
- // Original behavior with diff view
- cline.diffViewProvider.editType = "modify"
- await cline.diffViewProvider.open(relPath)
- await cline.diffViewProvider.update(originalContent!, true)
- cline.diffViewProvider.scrollToFirstDiff()
-
- // Call saveChanges to update the DiffViewProvider properties
- await cline.diffViewProvider.saveChanges(diagnosticsEnabled, writeDelayMs)
- }
- }
-
- // Track file edit operation
- await cline.fileContextTracker.trackFileContext(relPath, "roo_edited" as RecordSource)
-
- // Used to determine if we should wait for busy terminal to update before sending api request
- cline.didEditFile = true
- let partFailHint = ""
-
- if (successCount < diffItems.length) {
- partFailHint = `Unable to apply all diff parts to file: ${absolutePath}`
- }
-
- // Get the formatted response message
- const message = await cline.diffViewProvider.pushToolWriteResult(cline, cline.cwd, !fileExists)
-
- if (partFailHint) {
- results.push(partFailHint + "\n" + message)
- } else {
- results.push(message)
- }
-
- await cline.diffViewProvider.reset()
- } catch (error) {
- const errorMsg = error instanceof Error ? error.message : String(error)
- updateOperationResult(relPath, {
- status: "error",
- error: `Error processing ${relPath}: ${errorMsg}`,
- })
- results.push(`Error processing ${relPath}: ${errorMsg}`)
- }
- }
-
- // Add filtered operation errors to results
- if (filteredOperationErrors.length > 0) {
- results.push(...filteredOperationErrors)
- }
-
- // Report all diff errors at once if any
- if (allDiffErrors.length > 0) {
- await cline.say("diff_error", allDiffErrors.join("\n"))
- }
-
- // Check for single SEARCH/REPLACE block warning
- let totalSearchBlocks = 0
- for (const operation of operations) {
- for (const diffItem of operation.diff) {
- const searchBlocks = (diffItem.content.match(/<<<<<<< SEARCH/g) || []).length
- totalSearchBlocks += searchBlocks
- }
- }
-
- // Check protocol for notice formatting - reuse the task's locked protocol
- const noticeProtocol = resolveToolProtocol(
- cline.apiConfiguration,
- cline.api.getModel().info,
- cline.taskToolProtocol,
- )
- const singleBlockNotice =
- totalSearchBlocks === 1
- ? isNativeProtocol(noticeProtocol)
- ? "\n" +
- JSON.stringify({
- notice: "Making multiple related changes in a single apply_diff is more efficient. If other changes are needed in this file, please include them as additional SEARCH/REPLACE blocks.",
- })
- : "\nMaking multiple related changes in a single apply_diff is more efficient. If other changes are needed in this file, please include them as additional SEARCH/REPLACE blocks."
- : ""
-
- // Push the final result combining all operation results
- pushToolResult(results.join("\n\n") + singleBlockNotice)
- cline.processQueuedMessages()
- return
- } catch (error) {
- await handleError("applying diff", error)
- await cline.diffViewProvider.reset()
- cline.processQueuedMessages()
- return
- }
+ return applyDiffToolClass.handle(cline, block as ToolUse<"apply_diff">, {
+ askApproval,
+ handleError,
+ pushToolResult,
+ })
}
diff --git a/src/core/tools/NewTaskTool.ts b/src/core/tools/NewTaskTool.ts
index c5607d2a85..fd208128da 100644
--- a/src/core/tools/NewTaskTool.ts
+++ b/src/core/tools/NewTaskTool.ts
@@ -20,17 +20,9 @@ interface NewTaskParams {
export class NewTaskTool extends BaseTool<"new_task"> {
readonly name = "new_task" as const
- parseLegacy(params: Partial>): NewTaskParams {
- return {
- mode: params.mode || "",
- message: params.message || "",
- todos: params.todos,
- }
- }
-
async execute(params: NewTaskParams, task: Task, callbacks: ToolCallbacks): Promise {
const { mode, message, todos } = params
- const { askApproval, handleError, pushToolResult, toolProtocol, toolCallId } = callbacks
+ const { askApproval, handleError, pushToolResult } = callbacks
try {
// Validate required parameters.
@@ -147,9 +139,9 @@ export class NewTaskTool extends BaseTool<"new_task"> {
const partialMessage = JSON.stringify({
tool: "newTask",
- mode: this.removeClosingTag("mode", mode, block.partial),
- content: this.removeClosingTag("message", message, block.partial),
- todos: this.removeClosingTag("todos", todos, block.partial),
+ mode: mode ?? "",
+ content: message ?? "",
+ todos: todos,
})
await task.ask("tool", partialMessage, block.partial).catch(() => {})
diff --git a/src/core/tools/ReadFileTool.ts b/src/core/tools/ReadFileTool.ts
index 2bba6bc6cd..1e20ac5cb3 100644
--- a/src/core/tools/ReadFileTool.ts
+++ b/src/core/tools/ReadFileTool.ts
@@ -3,7 +3,7 @@ import * as fs from "fs/promises"
import { isBinaryFile } from "isbinaryfile"
import type { FileEntry, LineRange } from "@roo-code/types"
-import { type ClineSayTool, isNativeProtocol, ANTHROPIC_DEFAULT_MAX_TOKENS } from "@roo-code/types"
+import { type ClineSayTool, ANTHROPIC_DEFAULT_MAX_TOKENS } from "@roo-code/types"
import { Task } from "../task/Task"
import { formatResponse } from "../prompts/responses"
@@ -16,8 +16,6 @@ import { countFileLines } from "../../integrations/misc/line-counter"
import { readLines } from "../../integrations/misc/read-lines"
import { extractTextFromFile, addLineNumbers, getSupportedBinaryFormats } from "../../integrations/misc/extract-text"
import { parseSourceCodeDefinitionsForFile } from "../../services/tree-sitter"
-import { parseXml } from "../../utils/xml"
-import { resolveToolProtocol } from "../../utils/resolveToolProtocol"
import type { ToolUse } from "../../shared/tools"
import {
@@ -39,7 +37,6 @@ interface FileResult {
error?: string
notice?: string
lineRanges?: LineRange[]
- xmlContent?: string
nativeContent?: string
imageDataUrl?: string
feedbackText?: string
@@ -49,78 +46,17 @@ interface FileResult {
export class ReadFileTool extends BaseTool<"read_file"> {
readonly name = "read_file" as const
- parseLegacy(params: Partial>): { files: FileEntry[] } {
- const argsXmlTag = params.args
- const legacyPath = params.path
- const legacyStartLineStr = params.start_line
- const legacyEndLineStr = params.end_line
-
- const fileEntries: FileEntry[] = []
-
- // XML args format
- if (argsXmlTag) {
- const parsed = parseXml(argsXmlTag) as any
- const files = Array.isArray(parsed.file) ? parsed.file : [parsed.file].filter(Boolean)
-
- for (const file of files) {
- if (!file.path) continue
-
- const fileEntry: FileEntry = {
- path: file.path,
- lineRanges: [],
- }
-
- if (file.line_range) {
- const ranges = Array.isArray(file.line_range) ? file.line_range : [file.line_range]
- for (const range of ranges) {
- const match = String(range).match(/(\d+)-(\d+)/)
- if (match) {
- const [, start, end] = match.map(Number)
- if (!isNaN(start) && !isNaN(end)) {
- fileEntry.lineRanges?.push({ start, end })
- }
- }
- }
- }
- fileEntries.push(fileEntry)
- }
-
- return { files: fileEntries }
- }
-
- // Legacy single file path
- if (legacyPath) {
- const fileEntry: FileEntry = {
- path: legacyPath,
- lineRanges: [],
- }
-
- if (legacyStartLineStr && legacyEndLineStr) {
- const start = parseInt(legacyStartLineStr, 10)
- const end = parseInt(legacyEndLineStr, 10)
- if (!isNaN(start) && !isNaN(end) && start > 0 && end > 0) {
- fileEntry.lineRanges?.push({ start, end })
- }
- }
- fileEntries.push(fileEntry)
- }
-
- return { files: fileEntries }
- }
-
async execute(params: { files: FileEntry[] }, task: Task, callbacks: ToolCallbacks): Promise {
- const { handleError, pushToolResult, toolProtocol } = callbacks
+ const { handleError, pushToolResult } = callbacks
const fileEntries = params.files
const modelInfo = task.api.getModel().info
- // Use the task's locked protocol for consistent output formatting throughout the task
- const protocol = resolveToolProtocol(task.apiConfiguration, modelInfo, task.taskToolProtocol)
- const useNative = isNativeProtocol(protocol)
+ const useNative = true
if (!fileEntries || fileEntries.length === 0) {
task.consecutiveMistakeCount++
task.recordToolError("read_file")
- const errorMsg = await task.sayAndCreateMissingParamError("read_file", "args (containing valid file paths)")
- const errorResult = useNative ? `Error: ${errorMsg}` : `