Skip to content

Commit d5f1011

Browse files
committed
Use @roo-code/core
1 parent 473a189 commit d5f1011

File tree

19 files changed

+641
-202
lines changed

19 files changed

+641
-202
lines changed

pnpm-lock.yaml

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/assistant-message/NativeToolCallParser.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
import { parseJSON } from "partial-json"
2+
13
import { type ToolName, toolNames, type FileEntry } from "@roo-code/types"
4+
import { customToolRegistry } from "@roo-code/core"
5+
26
import {
37
type ToolUse,
48
type McpToolUse,
59
type ToolParamName,
6-
toolParamNames,
710
type NativeToolArgs,
11+
toolParamNames,
812
} from "../../shared/tools"
913
import { resolveToolAlias } from "../prompts/tools/filter-tools-for-mode"
10-
import { parseJSON } from "partial-json"
1114
import type {
1215
ApiStreamToolCallStartChunk,
1316
ApiStreamToolCallDeltaChunk,
@@ -556,25 +559,28 @@ export class NativeToolCallParser {
556559
name: TName
557560
arguments: string
558561
}): ToolUse<TName> | McpToolUse | null {
562+
// console.log(`parseToolCall -> ${JSON.stringify(toolCall, null, 2)}`)
563+
559564
// Check if this is a dynamic MCP tool (mcp--serverName--toolName)
560565
const mcpPrefix = MCP_TOOL_PREFIX + MCP_TOOL_SEPARATOR
566+
561567
if (typeof toolCall.name === "string" && toolCall.name.startsWith(mcpPrefix)) {
562568
return this.parseDynamicMcpTool(toolCall)
563569
}
564570

565571
// Resolve tool alias to canonical name (e.g., "edit_file" -> "apply_diff", "temp_edit_file" -> "search_and_replace")
566572
const resolvedName = resolveToolAlias(toolCall.name as string) as TName
567573

568-
// Validate tool name (after alias resolution)
569-
if (!toolNames.includes(resolvedName as ToolName)) {
574+
// Validate tool name (after alias resolution).
575+
if (!toolNames.includes(resolvedName as ToolName) && !customToolRegistry.has(resolvedName)) {
570576
console.error(`Invalid tool name: ${toolCall.name} (resolved: ${resolvedName})`)
571577
console.error(`Valid tool names:`, toolNames)
572578
return null
573579
}
574580

575581
try {
576582
// Parse the arguments JSON string
577-
const args = JSON.parse(toolCall.arguments)
583+
const args = toolCall.arguments === "" ? {} : JSON.parse(toolCall.arguments)
578584

579585
// Build legacy params object for backward compatibility with XML protocol and UI.
580586
// Native execution path uses nativeArgs instead, which has proper typing.
@@ -589,7 +595,7 @@ export class NativeToolCallParser {
589595
}
590596

591597
// Validate parameter name
592-
if (!toolParamNames.includes(key as ToolParamName)) {
598+
if (!toolParamNames.includes(key as ToolParamName) && !customToolRegistry.has(resolvedName)) {
593599
console.warn(`Unknown parameter '${key}' for tool '${resolvedName}'`)
594600
console.warn(`Valid param names:`, toolParamNames)
595601
continue
@@ -786,6 +792,14 @@ export class NativeToolCallParser {
786792
break
787793

788794
default:
795+
if (customToolRegistry.has(resolvedName)) {
796+
nativeArgs = args as NativeArgsFor<TName>
797+
} else {
798+
console.error(`Unhandled tool: ${resolvedName}`)
799+
console.error(`Valid tool names:`, toolNames)
800+
console.error(`Custom tool names:`, customToolRegistry.list())
801+
}
802+
789803
break
790804
}
791805

@@ -802,6 +816,8 @@ export class NativeToolCallParser {
802816
result.originalName = toolCall.name
803817
}
804818

819+
console.log(`parseToolCall -> result: ${JSON.stringify(result, null, 2)}`)
820+
805821
return result
806822
} catch (error) {
807823
console.error(

src/core/assistant-message/presentAssistantMessage.ts

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@ import { Anthropic } from "@anthropic-ai/sdk"
44

55
import type { ToolName, ClineAsk, ToolProgressStatus } from "@roo-code/types"
66
import { TelemetryService } from "@roo-code/telemetry"
7+
import { customToolRegistry } from "@roo-code/core"
8+
9+
import { t } from "../../i18n"
710

811
import { defaultModeSlug, getModeBySlug } from "../../shared/modes"
912
import type { ToolParamName, ToolResponse, ToolUse, McpToolUse } from "../../shared/tools"
10-
import { Package } from "../../shared/package"
11-
import { t } from "../../i18n"
13+
import { experiments, EXPERIMENT_IDS } from "../../shared/experiments"
14+
1215
import { AskIgnoredError } from "../task/AskIgnoredError"
16+
import { Task } from "../task/Task"
1317

1418
import { fetchInstructionsTool } from "../tools/FetchInstructionsTool"
1519
import { listFilesTool } from "../tools/ListFilesTool"
@@ -30,17 +34,14 @@ import { askFollowupQuestionTool } from "../tools/AskFollowupQuestionTool"
3034
import { switchModeTool } from "../tools/SwitchModeTool"
3135
import { attemptCompletionTool, AttemptCompletionCallbacks } from "../tools/AttemptCompletionTool"
3236
import { newTaskTool } from "../tools/NewTaskTool"
33-
3437
import { updateTodoListTool } from "../tools/UpdateTodoListTool"
3538
import { runSlashCommandTool } from "../tools/RunSlashCommandTool"
3639
import { generateImageTool } from "../tools/GenerateImageTool"
37-
38-
import { formatResponse } from "../prompts/responses"
40+
import { applyDiffTool as applyDiffToolClass } from "../tools/ApplyDiffTool"
3941
import { validateToolUse } from "../tools/validateToolUse"
40-
import { Task } from "../task/Task"
4142
import { codebaseSearchTool } from "../tools/CodebaseSearchTool"
42-
import { experiments, EXPERIMENT_IDS } from "../../shared/experiments"
43-
import { applyDiffTool as applyDiffToolClass } from "../tools/ApplyDiffTool"
43+
44+
import { formatResponse } from "../prompts/responses"
4445

4546
/**
4647
* Processes and presents assistant message content to the user interface.
@@ -353,7 +354,7 @@ export async function presentAssistantMessage(cline: Task) {
353354
case "tool_use": {
354355
// Fetch state early so it's available for toolDescription and validation
355356
const state = await cline.providerRef.deref()?.getState()
356-
const { mode, customModes, experiments: stateExperiments, apiConfiguration } = state ?? {}
357+
const { mode, customModes, experiments: stateExperiments } = state ?? {}
357358

358359
const toolDescription = (): string => {
359360
switch (block.name) {
@@ -731,6 +732,8 @@ export async function presentAssistantMessage(cline: Task) {
731732
// This prevents the stream from being interrupted with "Response interrupted by tool use result"
732733
// which would cause the extension to appear to hang
733734
const errorContent = formatResponse.toolError(error.message, toolProtocol)
735+
console.error(`[presentAssistantMessage] errorContent: ${errorContent}`)
736+
734737
if (toolProtocol === TOOL_PROTOCOL.NATIVE && toolCallId) {
735738
// For native protocol, push tool_result directly without setting didAlreadyUseTool
736739
cline.userMessageContent.push({
@@ -743,6 +746,7 @@ export async function presentAssistantMessage(cline: Task) {
743746
// For XML protocol, use the standard pushToolResult
744747
pushToolResult(errorContent)
745748
}
749+
746750
break
747751
}
748752
}
@@ -1043,9 +1047,8 @@ export async function presentAssistantMessage(cline: Task) {
10431047
})
10441048
break
10451049
default: {
1046-
// Handle unknown/invalid tool names
1050+
// Handle unknown/invalid tool names OR custom tools
10471051
// This is critical for native protocol where every tool_use MUST have a tool_result
1048-
// Note: This case should rarely be reached since validateToolUse now checks for unknown tools
10491052

10501053
// CRITICAL: Don't process partial blocks for unknown tools - just let them stream in.
10511054
// If we try to show errors for partial blocks, we'd show the error on every streaming chunk,
@@ -1054,6 +1057,45 @@ export async function presentAssistantMessage(cline: Task) {
10541057
break
10551058
}
10561059

1060+
const customTool = customToolRegistry.get(block.name)
1061+
1062+
if (customTool) {
1063+
try {
1064+
console.log(`executing customTool -> ${JSON.stringify(customTool, null, 2)}`)
1065+
let customToolArgs
1066+
1067+
if (customTool.parameters) {
1068+
try {
1069+
customToolArgs = customTool.parameters.parse(block.nativeArgs || block.params || {})
1070+
console.log(`customToolArgs -> ${JSON.stringify(customToolArgs, null, 2)}`)
1071+
} catch (parseParamsError) {
1072+
const message = `Custom tool "${block.name}" argument validation failed: ${parseParamsError.message}`
1073+
console.error(message)
1074+
cline.consecutiveMistakeCount++
1075+
await cline.say("error", message)
1076+
pushToolResult(formatResponse.toolError(message, toolProtocol))
1077+
break
1078+
}
1079+
}
1080+
1081+
console.log(`${customTool.name}.execute() -> ${JSON.stringify(customToolArgs, null, 2)}`)
1082+
1083+
const result = await customTool.execute(customToolArgs, {
1084+
mode: mode ?? defaultModeSlug,
1085+
task: cline,
1086+
})
1087+
1088+
pushToolResult(result)
1089+
cline.consecutiveMistakeCount = 0
1090+
} catch (executionError: any) {
1091+
cline.consecutiveMistakeCount++
1092+
await handleError(`executing custom tool "${block.name}"`, executionError)
1093+
}
1094+
1095+
break
1096+
}
1097+
1098+
// Not a custom tool - handle as unknown tool error
10571099
const errorMessage = `Unknown tool "${block.name}". This tool does not exist. Please use one of the available tools.`
10581100
cline.consecutiveMistakeCount++
10591101
cline.recordToolError(block.name as ToolName, errorMessage)

src/core/environment/__tests__/getEnvironmentDetails.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import type { Mock } from "vitest"
66

77
import { getEnvironmentDetails } from "../getEnvironmentDetails"
88
import { EXPERIMENT_IDS, experiments } from "../../../shared/experiments"
9-
import { defaultModeSlug, getFullModeDetails, getModeBySlug, isToolAllowedForMode } from "../../../shared/modes"
9+
import { getFullModeDetails } from "../../../shared/modes"
10+
import { isToolAllowedForMode } from "../../tools/validateToolUse"
1011
import { getApiMetrics } from "../../../shared/getApiMetrics"
1112
import { listFiles } from "../../../services/glob/list-files"
1213
import { TerminalRegistry } from "../../../integrations/terminal/TerminalRegistry"
@@ -51,6 +52,7 @@ vi.mock("../../../integrations/terminal/Terminal")
5152
vi.mock("../../../utils/path")
5253
vi.mock("../../../utils/git")
5354
vi.mock("../../prompts/responses")
55+
vi.mock("../../tools/validateToolUse")
5456

5557
describe("getEnvironmentDetails", () => {
5658
const mockCwd = "/test/path"

src/core/environment/getEnvironmentDetails.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { DEFAULT_TERMINAL_OUTPUT_CHARACTER_LIMIT } from "@roo-code/types"
1111
import { resolveToolProtocol } from "../../utils/resolveToolProtocol"
1212
import { EXPERIMENT_IDS, experiments as Experiments } from "../../shared/experiments"
1313
import { formatLanguage } from "../../shared/language"
14-
import { defaultModeSlug, getFullModeDetails, getModeBySlug, isToolAllowedForMode } from "../../shared/modes"
14+
import { defaultModeSlug, getFullModeDetails } from "../../shared/modes"
1515
import { getApiMetrics } from "../../shared/getApiMetrics"
1616
import { listFiles } from "../../services/glob/list-files"
1717
import { TerminalRegistry } from "../../integrations/terminal/TerminalRegistry"

src/core/prompts/system.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import * as vscode from "vscode"
22
import * as os from "os"
33

4-
import type { ModeConfig, PromptComponent, CustomModePrompts, TodoItem } from "@roo-code/types"
5-
6-
import type { SystemPromptSettings } from "./types"
4+
import {
5+
type ModeConfig,
6+
type PromptComponent,
7+
type CustomModePrompts,
8+
type TodoItem,
9+
getEffectiveProtocol,
10+
isNativeProtocol,
11+
} from "@roo-code/types"
12+
import { customToolRegistry, formatXml } from "@roo-code/core"
713

814
import { Mode, modes, defaultModeSlug, getModeBySlug, getGroupName, getModeSelection } from "../../shared/modes"
915
import { DiffStrategy } from "../../shared/tools"
@@ -15,8 +21,8 @@ import { CodeIndexManager } from "../../services/code-index/manager"
1521

1622
import { PromptVariables, loadSystemPromptFile } from "./sections/custom-system-prompt"
1723

24+
import type { SystemPromptSettings } from "./types"
1825
import { getToolDescriptionsForMode } from "./tools"
19-
import { getEffectiveProtocol, isNativeProtocol } from "@roo-code/types"
2026
import {
2127
getRulesSection,
2228
getSystemInfoSection,
@@ -98,7 +104,7 @@ async function generatePrompt(
98104
])
99105

100106
// Build tools catalog section only for XML protocol
101-
const toolsCatalog = isNativeProtocol(effectiveProtocol)
107+
const builtInToolsCatalog = isNativeProtocol(effectiveProtocol)
102108
? ""
103109
: `\n\n${getToolDescriptionsForMode(
104110
mode,
@@ -116,6 +122,18 @@ async function generatePrompt(
116122
modelId,
117123
)}`
118124

125+
let customToolsSection = ""
126+
127+
if (!isNativeProtocol(effectiveProtocol)) {
128+
const customTools = customToolRegistry.getAllSerialized()
129+
130+
if (customTools.length > 0) {
131+
customToolsSection = `\n\n${formatXml(customTools)}`
132+
}
133+
}
134+
135+
const toolsCatalog = builtInToolsCatalog + customToolsSection
136+
119137
const basePrompt = `${roleDefinition}
120138
121139
${markdownFormattingSection()}

src/core/prompts/tools/filter-tools-for-mode.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import type OpenAI from "openai"
22
import type { ModeConfig, ToolName, ToolGroup, ModelInfo } from "@roo-code/types"
3-
import { getModeBySlug, getToolsForMode, isToolAllowedForMode } from "../../../shared/modes"
3+
import { getModeBySlug, getToolsForMode } from "../../../shared/modes"
44
import { TOOL_GROUPS, ALWAYS_AVAILABLE_TOOLS, TOOL_ALIASES } from "../../../shared/tools"
55
import { defaultModeSlug } from "../../../shared/modes"
66
import type { CodeIndexManager } from "../../../services/code-index/manager"
77
import type { McpHub } from "../../../services/mcp/McpHub"
8+
import { isToolAllowedForMode } from "../../../core/tools/validateToolUse"
89

910
/**
1011
* Reverse lookup map - maps alias name to canonical tool name.

src/core/prompts/tools/index.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import type { ToolName, ModeConfig } from "@roo-code/types"
2+
import { shouldUseSingleFileRead } from "@roo-code/types"
23

34
import { TOOL_GROUPS, ALWAYS_AVAILABLE_TOOLS, DiffStrategy } from "../../../shared/tools"
5+
import { Mode, getModeConfig, getGroupName } from "../../../shared/modes"
6+
7+
import { isToolAllowedForMode } from "../../tools/validateToolUse"
8+
49
import { McpHub } from "../../../services/mcp/McpHub"
5-
import { Mode, getModeConfig, isToolAllowedForMode, getGroupName } from "../../../shared/modes"
10+
import { CodeIndexManager } from "../../../services/code-index/manager"
611

712
import { ToolArgs } from "./types"
813
import { getExecuteCommandDescription } from "./execute-command"
914
import { getReadFileDescription } from "./read-file"
1015
import { getSimpleReadFileDescription } from "./simple-read-file"
1116
import { getFetchInstructionsDescription } from "./fetch-instructions"
12-
import { shouldUseSingleFileRead } from "@roo-code/types"
1317
import { getWriteToFileDescription } from "./write-to-file"
1418
import { getSearchFilesDescription } from "./search-files"
1519
import { getListFilesDescription } from "./list-files"
@@ -24,7 +28,6 @@ import { getCodebaseSearchDescription } from "./codebase-search"
2428
import { getUpdateTodoListDescription } from "./update-todo-list"
2529
import { getRunSlashCommandDescription } from "./run-slash-command"
2630
import { getGenerateImageDescription } from "./generate-image"
27-
import { CodeIndexManager } from "../../../services/code-index/manager"
2831

2932
// Map of tool names to their description functions
3033
const toolDescriptionMap: Record<string, (args: ToolArgs) => string | undefined> = {

src/core/task/Task.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2568,6 +2568,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
25682568

25692569
// Get the index and replace partial with final
25702570
if (toolUseIndex !== undefined) {
2571+
console.log(
2572+
`[tool_call_partial] setting tool use at index ${toolUseIndex} with finalToolUse: ${JSON.stringify(finalToolUse, null, 2)}`,
2573+
)
2574+
25712575
this.assistantMessageContent[toolUseIndex] = finalToolUse
25722576
}
25732577

@@ -2727,6 +2731,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
27272731

27282732
// Get the index and replace partial with final
27292733
if (toolUseIndex !== undefined) {
2734+
console.log(
2735+
`[tool_call_end] setting tool use at index ${toolUseIndex} with finalToolUse: ${JSON.stringify(finalToolUse, null, 2)}`,
2736+
)
2737+
27302738
this.assistantMessageContent[toolUseIndex] = finalToolUse
27312739
}
27322740

0 commit comments

Comments
 (0)