diff --git a/src/core/diff/strategies/multi-file-search-replace.ts b/src/core/diff/strategies/multi-file-search-replace.ts
index 57503da5f4..ef7fbcf846 100644
--- a/src/core/diff/strategies/multi-file-search-replace.ts
+++ b/src/core/diff/strategies/multi-file-search-replace.ts
@@ -2,7 +2,8 @@ import { distance } from "fastest-levenshtein"
import { ToolProgressStatus } from "@roo-code/types"
import { addLineNumbers, everyLineHasLineNumbers, stripLineNumbers } from "../../../integrations/misc/extract-text"
-import { ToolUse, DiffStrategy, DiffResult } from "../../../shared/tools"
+import { DiffStrategy, DiffResult } from "../../../shared/tools"
+import { ToolDirective } from "../../message-parsing/directives/"
import { normalizeString } from "../../../utils/text-normalization"
const BUFFER_LINES = 40 // Number of extra context lines to show before and after matches
@@ -707,7 +708,7 @@ Each file requires its own path, start_line, and diff elements.
}
}
- getProgressStatus(toolUse: ToolUse, result?: DiffResult): ToolProgressStatus {
+ getProgressStatus(toolUse: ToolDirective, result?: DiffResult): ToolProgressStatus {
const diffContent = toolUse.params.diff
if (diffContent) {
const icon = "diff-multiple"
diff --git a/src/core/diff/strategies/multi-search-replace.ts b/src/core/diff/strategies/multi-search-replace.ts
index 9e740a6571..36c86f7531 100644
--- a/src/core/diff/strategies/multi-search-replace.ts
+++ b/src/core/diff/strategies/multi-search-replace.ts
@@ -5,7 +5,8 @@ import { distance } from "fastest-levenshtein"
import { ToolProgressStatus } from "@roo-code/types"
import { addLineNumbers, everyLineHasLineNumbers, stripLineNumbers } from "../../../integrations/misc/extract-text"
-import { ToolUse, DiffStrategy, DiffResult } from "../../../shared/tools"
+import { DiffStrategy, DiffResult } from "../../../shared/tools"
+import { ToolDirective } from "../../message-parsing/directives/"
import { normalizeString } from "../../../utils/text-normalization"
const BUFFER_LINES = 40 // Number of extra context lines to show before and after matches
@@ -609,11 +610,11 @@ Only use a single line of '=======' between search and replacement content, beca
}
}
- getProgressStatus(toolUse: ToolUse, result?: DiffResult): ToolProgressStatus {
- const diffContent = toolUse.params.diff
+ getProgressStatus(ToolDirective: ToolDirective, result?: DiffResult): ToolProgressStatus {
+ const diffContent = ToolDirective.params.diff
if (diffContent) {
const icon = "diff-multiple"
- if (toolUse.partial) {
+ if (ToolDirective.partial) {
if (Math.floor(diffContent.length / 10) % 10 === 0) {
const searchBlockCount = (diffContent.match(/SEARCH/g) || []).length
return { icon, text: `${searchBlockCount}` }
diff --git a/src/core/message-parsing/FallbackParser.ts b/src/core/message-parsing/FallbackParser.ts
index 59f94d6d95..d281640b15 100644
--- a/src/core/message-parsing/FallbackParser.ts
+++ b/src/core/message-parsing/FallbackParser.ts
@@ -1,7 +1,6 @@
-import { Directive } from "./directives"
+import { Directive, ToolDirective } from "./directives"
import { TextDirective, LogDirective } from "./directives"
import { toolNames } from "@roo-code/types"
-import { ToolUse } from "../../shared/tools"
export class FallbackParser {
static parse(assistantMessage: string): Directive[] {
@@ -76,14 +75,14 @@ export class FallbackParser {
}
}
- const toolUse: ToolUse = {
+ const ToolDirective: ToolDirective = {
type: "tool_use",
name: toolName as any,
params,
partial: !assistantMessage.includes(`${toolName}>`),
}
- contentBlocks.push(toolUse)
+ contentBlocks.push(ToolDirective)
return contentBlocks
}
}
diff --git a/src/core/message-parsing/__tests__/DirectiveStreamingParser.spec.ts b/src/core/message-parsing/__tests__/DirectiveStreamingParser.spec.ts
index d7ead213f9..2f3f21cd9a 100644
--- a/src/core/message-parsing/__tests__/DirectiveStreamingParser.spec.ts
+++ b/src/core/message-parsing/__tests__/DirectiveStreamingParser.spec.ts
@@ -1,7 +1,6 @@
import { suite, test, expect } from "vitest"
import { DirectiveStreamingParser } from "../DirectiveStreamingParser"
-import { TextDirective, LogDirective } from "../directives"
-import { ToolUse } from "../../../shared/tools"
+import { ToolDirective, TextDirective, LogDirective } from "../directives"
suite("DirectiveStreamingParser", () => {
test("should parse plain text content", () => {
@@ -51,7 +50,7 @@ suite("DirectiveStreamingParser", () => {
name: "read_file",
params: { path: "src/app.ts" },
partial: false,
- } as ToolUse,
+ } as ToolDirective,
])
})
@@ -70,7 +69,7 @@ suite("DirectiveStreamingParser", () => {
name: "apply_diff",
params: { path: "src/file.ts", diff: "diff content" },
partial: false,
- } as ToolUse,
+ } as ToolDirective,
{
type: "text",
content: "More text",
@@ -88,7 +87,7 @@ suite("DirectiveStreamingParser", () => {
name: "write_to_file",
params: { path: "src/newfile.ts", content: "Some content" },
partial: true,
- } as ToolUse,
+ } as ToolDirective,
])
})
})
diff --git a/src/core/message-parsing/__tests__/DirectiveStreamingParser.test.ts b/src/core/message-parsing/__tests__/DirectiveStreamingParser.test.ts
index d7ead213f9..2f3f21cd9a 100644
--- a/src/core/message-parsing/__tests__/DirectiveStreamingParser.test.ts
+++ b/src/core/message-parsing/__tests__/DirectiveStreamingParser.test.ts
@@ -1,7 +1,6 @@
import { suite, test, expect } from "vitest"
import { DirectiveStreamingParser } from "../DirectiveStreamingParser"
-import { TextDirective, LogDirective } from "../directives"
-import { ToolUse } from "../../../shared/tools"
+import { ToolDirective, TextDirective, LogDirective } from "../directives"
suite("DirectiveStreamingParser", () => {
test("should parse plain text content", () => {
@@ -51,7 +50,7 @@ suite("DirectiveStreamingParser", () => {
name: "read_file",
params: { path: "src/app.ts" },
partial: false,
- } as ToolUse,
+ } as ToolDirective,
])
})
@@ -70,7 +69,7 @@ suite("DirectiveStreamingParser", () => {
name: "apply_diff",
params: { path: "src/file.ts", diff: "diff content" },
partial: false,
- } as ToolUse,
+ } as ToolDirective,
{
type: "text",
content: "More text",
@@ -88,7 +87,7 @@ suite("DirectiveStreamingParser", () => {
name: "write_to_file",
params: { path: "src/newfile.ts", content: "Some content" },
partial: true,
- } as ToolUse,
+ } as ToolDirective,
])
})
})
diff --git a/src/core/message-parsing/__tests__/parseAssistantMessage.test.ts b/src/core/message-parsing/__tests__/parseAssistantMessage.test.ts
index def4d21eb6..12bd04c3f9 100644
--- a/src/core/message-parsing/__tests__/parseAssistantMessage.test.ts
+++ b/src/core/message-parsing/__tests__/parseAssistantMessage.test.ts
@@ -1,8 +1,6 @@
// npx jest src/core/assistant-message/__tests__/parseAssistantMessage.test.ts
-import { TextDirective } from "../directives"
-import { ToolUse } from "../../../shared/tools"
-
+import { TextDirective, ToolDirective } from "../directives"
import { AssistantMessageContent, parseAssistantMessage as parseAssistantMessageV1 } from "../parseAssistantMessage"
import { parseAssistantMessageV2 } from "../parseAssistantMessageV2"
@@ -55,11 +53,11 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
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)
+ const ToolDirective = result[0] as ToolDirective
+ expect(ToolDirective.type).toBe("tool_use")
+ expect(ToolDirective.name).toBe("read_file")
+ expect(ToolDirective.params.path).toBe("src/file.ts")
+ expect(ToolDirective.partial).toBe(false)
})
it("should parse a tool use with multiple parameters", () => {
@@ -68,13 +66,13 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
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.params.start_line).toBe("10")
- expect(toolUse.params.end_line).toBe("20")
- expect(toolUse.partial).toBe(false)
+ const ToolDirective = result[0] as ToolDirective
+ expect(ToolDirective.type).toBe("tool_use")
+ expect(ToolDirective.name).toBe("read_file")
+ expect(ToolDirective.params.path).toBe("src/file.ts")
+ expect(ToolDirective.params.start_line).toBe("10")
+ expect(ToolDirective.params.end_line).toBe("20")
+ expect(ToolDirective.partial).toBe(false)
})
it("should mark tool use as partial when it's not closed", () => {
@@ -82,11 +80,11 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
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(true)
+ const ToolDirective = result[0] as ToolDirective
+ expect(ToolDirective.type).toBe("tool_use")
+ expect(ToolDirective.name).toBe("read_file")
+ expect(ToolDirective.params.path).toBe("src/file.ts")
+ expect(ToolDirective.partial).toBe(true)
})
it("should handle a partial parameter in a tool use", () => {
@@ -94,11 +92,11 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
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(true)
+ const ToolDirective = result[0] as ToolDirective
+ expect(ToolDirective.type).toBe("tool_use")
+ expect(ToolDirective.name).toBe("read_file")
+ expect(ToolDirective.params.path).toBe("src/file.ts")
+ expect(ToolDirective.partial).toBe(true)
})
})
@@ -114,11 +112,11 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
expect(textContent.content).toBe("Here's the file content:")
expect(textContent.partial).toBe(false)
- const toolUse = result[1] 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)
+ const ToolDirective = result[1] as ToolDirective
+ expect(ToolDirective.type).toBe("tool_use")
+ expect(ToolDirective.name).toBe("read_file")
+ expect(ToolDirective.params.path).toBe("src/file.ts")
+ expect(ToolDirective.partial).toBe(false)
})
it("should parse a tool use followed by text", () => {
@@ -127,11 +125,11 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
expect(result).toHaveLength(2)
- 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)
+ const ToolDirective = result[0] as ToolDirective
+ expect(ToolDirective.type).toBe("tool_use")
+ expect(ToolDirective.name).toBe("read_file")
+ expect(ToolDirective.params.path).toBe("src/file.ts")
+ expect(ToolDirective.partial).toBe(false)
const textContent = result[1] as TextDirective
expect(textContent.type).toBe("text")
@@ -150,15 +148,15 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
expect((result[0] as TextDirective).content).toBe("First file:")
expect(result[1].type).toBe("tool_use")
- expect((result[1] as ToolUse).name).toBe("read_file")
- expect((result[1] as ToolUse).params.path).toBe("src/file1.ts")
+ expect((result[1] as ToolDirective).name).toBe("read_file")
+ expect((result[1] as ToolDirective).params.path).toBe("src/file1.ts")
expect(result[2].type).toBe("text")
expect((result[2] as TextDirective).content).toBe("Second file:")
expect(result[3].type).toBe("tool_use")
- expect((result[3] as ToolUse).name).toBe("read_file")
- expect((result[3] as ToolUse).params.path).toBe("src/file2.ts")
+ expect((result[3] as ToolDirective).name).toBe("read_file")
+ expect((result[3] as ToolDirective).params.path).toBe("src/file2.ts")
})
})
@@ -174,15 +172,15 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
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("src/file.ts")
- expect(toolUse.params.line_count).toBe("5")
- expect(toolUse.params.content).toContain("function example()")
- expect(toolUse.params.content).toContain("// This has XML-like content: ")
- expect(toolUse.params.content).toContain("return true;")
- expect(toolUse.partial).toBe(false)
+ const ToolDirective = result[0] as ToolDirective
+ expect(ToolDirective.type).toBe("tool_use")
+ expect(ToolDirective.name).toBe("write_to_file")
+ expect(ToolDirective.params.path).toBe("src/file.ts")
+ expect(ToolDirective.params.line_count).toBe("5")
+ expect(ToolDirective.params.content).toContain("function example()")
+ expect(ToolDirective.params.content).toContain("// This has XML-like content: ")
+ expect(ToolDirective.params.content).toContain("return true;")
+ expect(ToolDirective.partial).toBe(false)
})
it("should handle empty messages", () => {
@@ -206,11 +204,11 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
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("browser_action")
- expect(Object.keys(toolUse.params).length).toBe(0)
- expect(toolUse.partial).toBe(false)
+ const ToolDirective = result[0] as ToolDirective
+ expect(ToolDirective.type).toBe("tool_use")
+ expect(ToolDirective.name).toBe("browser_action")
+ expect(Object.keys(ToolDirective.params).length).toBe(0)
+ expect(ToolDirective.partial).toBe(false)
})
it("should handle nested tool tags that aren't actually nested", () => {
@@ -220,11 +218,11 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
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("execute_command")
- expect(toolUse.params.command).toBe("echo 'test.txt'")
- expect(toolUse.partial).toBe(false)
+ const ToolDirective = result[0] as ToolDirective
+ expect(ToolDirective.type).toBe("tool_use")
+ expect(ToolDirective.name).toBe("execute_command")
+ expect(ToolDirective.params.command).toBe("echo 'test.txt'")
+ expect(ToolDirective.partial).toBe(false)
})
it("should handle a tool use with a parameter containing XML-like content", () => {
@@ -232,12 +230,12 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
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("search_files")
- expect(toolUse.params.regex).toBe("
.*
")
- expect(toolUse.params.path).toBe("src")
- expect(toolUse.partial).toBe(false)
+ const ToolDirective = result[0] as ToolDirective
+ expect(ToolDirective.type).toBe("tool_use")
+ expect(ToolDirective.name).toBe("search_files")
+ expect(ToolDirective.params.regex).toBe(".*
")
+ expect(ToolDirective.params.path).toBe("src")
+ expect(ToolDirective.partial).toBe(false)
})
it("should handle consecutive tool uses without text in between", () => {
@@ -247,17 +245,17 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
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 ToolDirective1 = result[0] as ToolDirective
+ expect(ToolDirective1.type).toBe("tool_use")
+ expect(ToolDirective1.name).toBe("read_file")
+ expect(ToolDirective1.params.path).toBe("file1.ts")
+ expect(ToolDirective1.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)
+ const ToolDirective2 = result[1] as ToolDirective
+ expect(ToolDirective2.type).toBe("tool_use")
+ expect(ToolDirective2.name).toBe("read_file")
+ expect(ToolDirective2.params.path).toBe("file2.ts")
+ expect(ToolDirective2.partial).toBe(false)
})
it("should handle whitespace in parameters", () => {
@@ -265,11 +263,11 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
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)
+ const ToolDirective = result[0] as ToolDirective
+ expect(ToolDirective.type).toBe("tool_use")
+ expect(ToolDirective.name).toBe("read_file")
+ expect(ToolDirective.params.path).toBe("src/file.ts")
+ expect(ToolDirective.partial).toBe(false)
})
it("should handle multi-line parameters", () => {
@@ -281,15 +279,15 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
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.params.line_count).toBe("3")
- expect(toolUse.partial).toBe(false)
+ const ToolDirective = result[0] as ToolDirective
+ expect(ToolDirective.type).toBe("tool_use")
+ expect(ToolDirective.name).toBe("write_to_file")
+ expect(ToolDirective.params.path).toBe("file.ts")
+ expect(ToolDirective.params.content).toContain("line 1")
+ expect(ToolDirective.params.content).toContain("line 2")
+ expect(ToolDirective.params.content).toContain("line 3")
+ expect(ToolDirective.params.line_count).toBe("3")
+ expect(ToolDirective.partial).toBe(false)
})
it("should handle a complex message with multiple content types", () => {
@@ -318,7 +316,7 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
// First tool use (read_file)
expect(result[1].type).toBe("tool_use")
- expect((result[1] as ToolUse).name).toBe("read_file")
+ expect((result[1] as ToolDirective).name).toBe("read_file")
// Second text block
expect(result[2].type).toBe("text")
@@ -326,7 +324,7 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
// Second tool use (write_to_file)
expect(result[3].type).toBe("tool_use")
- expect((result[3] as ToolUse).name).toBe("write_to_file")
+ expect((result[3] as ToolDirective).name).toBe("write_to_file")
// Third text block
expect(result[4].type).toBe("text")
@@ -334,7 +332,7 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
// Third tool use (execute_command)
expect(result[5].type).toBe("tool_use")
- expect((result[5] as ToolUse).name).toBe("execute_command")
+ expect((result[5] as ToolDirective).name).toBe("execute_command")
})
})
})
diff --git a/src/core/message-parsing/directives/Directive.ts b/src/core/message-parsing/directives/Directive.ts
new file mode 100644
index 0000000000..113ae64cac
--- /dev/null
+++ b/src/core/message-parsing/directives/Directive.ts
@@ -0,0 +1,5 @@
+import { LogDirective } from "./LogDirective"
+import { TextDirective } from "./TextDirective"
+import { ToolDirective } from "./ToolDirective"
+
+export type Directive = TextDirective | ToolDirective | LogDirective
diff --git a/src/core/message-parsing/directives/ToolDirective.ts b/src/core/message-parsing/directives/ToolDirective.ts
new file mode 100644
index 0000000000..6a9437e67c
--- /dev/null
+++ b/src/core/message-parsing/directives/ToolDirective.ts
@@ -0,0 +1,10 @@
+import { ToolName } from "@roo-code/types"
+import { ToolParamName } from "../../../shared/tools"
+
+export interface ToolDirective {
+ type: "tool_use"
+ name: ToolName
+ // params is a partial record, allowing only some or none of the possible parameters to be used
+ params: Partial>
+ partial: boolean
+}
diff --git a/src/core/message-parsing/directives/index.ts b/src/core/message-parsing/directives/index.ts
index 62397abe18..36f7aa9260 100644
--- a/src/core/message-parsing/directives/index.ts
+++ b/src/core/message-parsing/directives/index.ts
@@ -1,8 +1,4 @@
-import { TextDirective, LogDirective } from "../directives"
-import { ToolUse } from "../../../shared/tools"
-
-export * from "./LogDirective"
-export * from "./TextDirective"
-
-export type ToolDirective = ToolUse
-export type Directive = TextDirective | ToolDirective | LogDirective
+export type { Directive } from "./Directive"
+export type { TextDirective } from "./TextDirective"
+export type { ToolDirective } from "./ToolDirective"
+export type { LogDirective } from "./LogDirective"
diff --git a/src/core/message-parsing/handlers/ToolDirectiveHandler.ts b/src/core/message-parsing/handlers/ToolDirectiveHandler.ts
index 505941f677..a480aebbb5 100644
--- a/src/core/message-parsing/handlers/ToolDirectiveHandler.ts
+++ b/src/core/message-parsing/handlers/ToolDirectiveHandler.ts
@@ -1,11 +1,12 @@
import * as sax from "sax"
import { BaseDirectiveHandler } from "./BaseDirectiveHandler"
import { ParseContext } from "../ParseContext"
-import { ToolUse, ToolParamName } from "../../../shared/tools"
+import { ToolParamName } from "../../../shared/tools"
+import { ToolDirective } from "../directives"
export class ToolDirectiveHandler extends BaseDirectiveHandler {
readonly tagName: string
- private currentToolUse?: ToolUse
+ private currentToolDirective?: ToolDirective
private currentParamName?: ToolParamName
private currentParamValue = ""
private currentContext: "param" | "none" = "none"
@@ -18,14 +19,14 @@ export class ToolDirectiveHandler extends BaseDirectiveHandler {
override onOpenTag(node: sax.Tag, context: ParseContext): void {
if (node.name === this.tagName) {
this.flushCurrentText(context)
- this.currentToolUse = {
+ this.currentToolDirective = {
type: "tool_use",
name: this.tagName as any,
params: {},
partial: true,
}
this.currentContext = "none"
- } else if (this.currentToolUse) {
+ } else if (this.currentToolDirective) {
this.currentParamName = node.name as ToolParamName
this.currentParamValue = ""
this.currentContext = "param"
@@ -33,13 +34,13 @@ export class ToolDirectiveHandler extends BaseDirectiveHandler {
}
override onCloseTag(tagName: string, context: ParseContext): void {
- if (tagName === this.tagName && this.currentToolUse) {
- this.currentToolUse.partial =
- context.hasIncompleteXml || Object.keys(this.currentToolUse.params).length === 0
- context.contentBlocks.push(this.currentToolUse)
- this.currentToolUse = undefined
- } else if (this.currentToolUse && this.currentParamName && tagName === this.currentParamName) {
- ;(this.currentToolUse.params as Record)[this.currentParamName] =
+ if (tagName === this.tagName && this.currentToolDirective) {
+ this.currentToolDirective.partial =
+ context.hasIncompleteXml || Object.keys(this.currentToolDirective.params).length === 0
+ context.contentBlocks.push(this.currentToolDirective)
+ this.currentToolDirective = undefined
+ } else if (this.currentToolDirective && this.currentParamName && tagName === this.currentParamName) {
+ ;(this.currentToolDirective.params as Record)[this.currentParamName] =
this.currentParamValue.trim()
this.currentParamName = undefined
this.currentParamValue = ""
@@ -48,19 +49,19 @@ export class ToolDirectiveHandler extends BaseDirectiveHandler {
}
override onText(text: string, context: ParseContext): void {
- if (this.currentContext === "param" && this.currentParamName && this.currentToolUse) {
+ if (this.currentContext === "param" && this.currentParamName && this.currentToolDirective) {
this.currentParamValue += text
}
}
override onEnd(context: ParseContext): void {
- if (this.currentToolUse) {
+ if (this.currentToolDirective) {
if (this.currentParamName && this.currentParamValue) {
- ;(this.currentToolUse.params as Record)[this.currentParamName] =
+ ;(this.currentToolDirective.params as Record)[this.currentParamName] =
this.currentParamValue.trim()
}
- this.currentToolUse.partial = true
- context.contentBlocks.push(this.currentToolUse)
+ this.currentToolDirective.partial = true
+ context.contentBlocks.push(this.currentToolDirective)
}
}
}
diff --git a/src/core/message-parsing/parseAssistantMessageV2.ts b/src/core/message-parsing/parseAssistantMessageV2.ts
index 7c8299e50b..3d18994dc1 100644
--- a/src/core/message-parsing/parseAssistantMessageV2.ts
+++ b/src/core/message-parsing/parseAssistantMessageV2.ts
@@ -1,8 +1,8 @@
import { type ToolName, toolNames } from "@roo-code/types"
-import { TextDirective } from "./directives"
-import { ToolUse, ToolParamName, toolParamNames } from "../../shared/tools"
+import { TextDirective, ToolDirective } from "./directives"
+import { ToolParamName, toolParamNames } from "../../shared/tools"
-export type AssistantMessageContent = TextDirective | ToolUse
+export type AssistantMessageContent = TextDirective | ToolDirective
/**
* Parses an assistant message string potentially containing mixed text and tool
@@ -14,10 +14,10 @@ export type AssistantMessageContent = TextDirective | ToolUse
* position, it checks if the substring *ending* at `i` matches any known
* opening or closing tags for tools or parameters using `startsWith` with an
* offset.
- * It uses pre-computed Maps (`toolUseOpenTags`, `toolParamOpenTags`) for quick
+ * It uses pre-computed Maps (`ToolDirectiveOpenTags`, `toolParamOpenTags`) for quick
* tag lookups.
* State is managed using indices (`currentTextContentStart`,
- * `currentToolUseStart`, `currentParamValueStart`) pointing to the start of the
+ * `currentToolDirectiveStart`, `currentParamValueStart`) pointing to the start of the
* current block within the original `assistantMessage` string.
*
* Slicing is used to extract content only when a block (text, parameter, or
@@ -32,7 +32,7 @@ export type AssistantMessageContent = TextDirective | ToolUse
*
* @param assistantMessage The raw string output from the assistant.
* @returns An array of `AssistantMessageContent` objects, which can be
- * `TextContent` or `ToolUse`. Blocks that were not fully closed by the
+ * `TextContent` or `ToolDirective`. Blocks that were not fully closed by the
* end of the input string will have their `partial` flag set to
* `true`.
*/
@@ -42,17 +42,17 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
let currentTextContentStart = 0 // Index where the current text block started.
let currentTextContent: TextDirective | undefined = undefined
- let currentToolUseStart = 0 // Index *after* the opening tag of the current tool use.
- let currentToolUse: ToolUse | undefined = undefined
+ let currentToolDirectiveStart = 0 // Index *after* the opening tag of the current tool use.
+ let currentToolDirective: ToolDirective | undefined = undefined
let currentParamValueStart = 0 // Index *after* the opening tag of the current param.
let currentParamName: ToolParamName | undefined = undefined
// Precompute tags for faster lookups.
- const toolUseOpenTags = new Map()
+ const ToolDirectiveOpenTags = new Map()
const toolParamOpenTags = new Map()
for (const name of toolNames) {
- toolUseOpenTags.set(`<${name}>`, name)
+ ToolDirectiveOpenTags.set(`<${name}>`, name)
}
for (const name of toolParamNames) {
@@ -65,7 +65,7 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
const currentCharIndex = i
// Parsing a tool parameter
- if (currentToolUse && currentParamName) {
+ if (currentToolDirective && currentParamName) {
const closeTag = `${currentParamName}>`
// Check if the string *ending* at index `i` matches the closing tag
if (
@@ -82,7 +82,7 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
currentCharIndex - closeTag.length + 1, // End before the closing tag.
)
.trim()
- currentToolUse.params[currentParamName] = value
+ currentToolDirective.params[currentParamName] = value
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 {
@@ -91,7 +91,7 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
}
// Parsing a tool use (but not a specific parameter).
- if (currentToolUse && !currentParamName) {
+ if (currentToolDirective && !currentParamName) {
// Ensure we are not inside a parameter already.
// Check if starting a new parameter.
let startedNewParam = false
@@ -113,7 +113,7 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
}
// Check if closing the current tool use.
- const toolCloseTag = `${currentToolUse.name}>`
+ const toolCloseTag = `${currentToolDirective.name}>`
if (
currentCharIndex >= toolCloseTag.length - 1 &&
@@ -123,7 +123,7 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
// Special handling for content params *before* finalizing the
// tool.
const toolContentSlice = assistantMessage.slice(
- currentToolUseStart, // From after the tool opening tag.
+ currentToolDirectiveStart, // From after the tool opening tag.
currentCharIndex - toolCloseTag.length + 1, // To before the tool closing tag.
)
@@ -134,8 +134,8 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
// 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.
+ currentToolDirective.name === "write_to_file" /* || currentToolDirective.name === "new_rule" */ &&
+ // !(contentParamName in currentToolDirective.params) && // Only if not already parsed.
toolContentSlice.includes(`<${contentParamName}>`) // Check if tag exists.
) {
const contentStartTag = `<${contentParamName}>`
@@ -150,13 +150,13 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
.slice(contentStart + contentStartTag.length, contentEnd)
.trim()
- currentToolUse.params[contentParamName] = contentValue
+ currentToolDirective.params[contentParamName] = contentValue
}
}
- currentToolUse.partial = false // Mark as complete.
- contentBlocks.push(currentToolUse)
- currentToolUse = undefined // Reset state.
+ currentToolDirective.partial = false // Mark as complete.
+ contentBlocks.push(currentToolDirective)
+ currentToolDirective = undefined // Reset state.
currentTextContentStart = currentCharIndex + 1 // Potential text starts after this tag.
continue // Move to next char.
}
@@ -167,11 +167,11 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
}
// Parsing text / looking for tool start.
- if (!currentToolUse) {
+ if (!currentToolDirective) {
// Check if starting a new tool use.
let startedNewTool = false
- for (const [tag, toolName] of toolUseOpenTags.entries()) {
+ for (const [tag, toolName] of ToolDirectiveOpenTags.entries()) {
if (
currentCharIndex >= tag.length - 1 &&
assistantMessage.startsWith(tag, currentCharIndex - tag.length + 1)
@@ -211,14 +211,14 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
}
// Start the new tool use.
- currentToolUse = {
+ currentToolDirective = {
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.
+ currentToolDirectiveStart = currentCharIndex + 1 // Tool content starts after the opening tag.
startedNewTool = true
break
@@ -250,17 +250,17 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
}
// Finalize any open parameter within an open tool use.
- if (currentToolUse && currentParamName) {
- currentToolUse.params[currentParamName] = assistantMessage
+ if (currentToolDirective && currentParamName) {
+ currentToolDirective.params[currentParamName] = assistantMessage
.slice(currentParamValueStart) // From param start to end of string.
.trim()
// Tool use remains partial.
}
// Finalize any open tool use (which might contain the finalized partial param).
- if (currentToolUse) {
+ if (currentToolDirective) {
// Tool use is partial because the loop finished before its closing tag.
- contentBlocks.push(currentToolUse)
+ contentBlocks.push(currentToolDirective)
}
// Finalize any trailing text content.
// Only possible if a tool use wasn't open at the very end.
diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts
index 4d4bab3179..d4bddd9394 100644
--- a/src/core/task/Task.ts
+++ b/src/core/task/Task.ts
@@ -65,7 +65,7 @@ import { SYSTEM_PROMPT } from "../prompts/system"
import { ToolRepetitionDetector } from "../tools/ToolRepetitionDetector"
import { FileContextTracker } from "../context-tracking/FileContextTracker"
import { RooIgnoreController } from "../ignore/RooIgnoreController"
-import { presentAssistantMessage } from "../assistant-message"
+import { presentAssistantMessage } from "../message-parsing"
import { truncateConversationIfNeeded } from "../sliding-window"
import { ClineProvider } from "../webview/ClineProvider"
import { MultiSearchReplaceDiffStrategy } from "../diff/strategies/multi-search-replace"
@@ -85,8 +85,7 @@ import { ApiMessage } from "../task-persistence/apiMessages"
import { getMessagesSinceLastSummary, summarizeConversation } from "../condense"
import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning"
import { LogManager } from "../logging"
-import { Directive } from "../assistant-message"
-import { DirectiveStreamingParser } from "../assistant-message"
+import { Directive, DirectiveStreamingParser } from "../message-parsing"
export type ClineEvents = {
message: [{ action: "created" | "updated"; message: ClineMessage }]
diff --git a/src/core/tools/ToolRepetitionDetector.ts b/src/core/tools/ToolRepetitionDetector.ts
index a82574ba0e..ebdaa13d1c 100644
--- a/src/core/tools/ToolRepetitionDetector.ts
+++ b/src/core/tools/ToolRepetitionDetector.ts
@@ -1,5 +1,5 @@
-import { ToolUse } from "../../shared/tools"
import { t } from "../../i18n"
+import { ToolDirective } from "../message-parsing/directives"
/**
* Class for detecting consecutive identical tool calls
@@ -22,10 +22,10 @@ export class ToolRepetitionDetector {
* Checks if the current tool call is identical to the previous one
* and determines if execution should be allowed
*
- * @param currentToolCallBlock ToolUse object representing the current tool call
+ * @param currentToolCallBlock ToolDirective object representing the current tool call
* @returns Object indicating if execution is allowed and a message to show if not
*/
- public check(currentToolCallBlock: ToolUse): {
+ public check(currentToolCallBlock: ToolDirective): {
allowExecution: boolean
askUser?: {
messageKey: string
@@ -33,7 +33,7 @@ export class ToolRepetitionDetector {
}
} {
// Serialize the block to a canonical JSON string for comparison
- const currentToolCallJson = this.serializeToolUse(currentToolCallBlock)
+ const currentToolCallJson = this.serializeToolDirective(currentToolCallBlock)
// Compare with previous tool call
if (this.previousToolCallJson === currentToolCallJson) {
@@ -64,28 +64,28 @@ export class ToolRepetitionDetector {
}
/**
- * Serializes a ToolUse object into a canonical JSON string for comparison
+ * Serializes a ToolDirective object into a canonical JSON string for comparison
*
- * @param toolUse The ToolUse object to serialize
+ * @param ToolDirective The ToolDirective object to serialize
* @returns JSON string representation of the tool use with sorted parameter keys
*/
- private serializeToolUse(toolUse: ToolUse): string {
+ private serializeToolDirective(ToolDirective: ToolDirective): string {
// Create a new parameters object with alphabetically sorted keys
const sortedParams: Record = {}
// Get parameter keys and sort them alphabetically
- const sortedKeys = Object.keys(toolUse.params).sort()
+ const sortedKeys = Object.keys(ToolDirective.params).sort()
// Populate the sorted parameters object in a type-safe way
for (const key of sortedKeys) {
- if (Object.prototype.hasOwnProperty.call(toolUse.params, key)) {
- sortedParams[key] = toolUse.params[key as keyof typeof toolUse.params]
+ if (Object.prototype.hasOwnProperty.call(ToolDirective.params, key)) {
+ sortedParams[key] = ToolDirective.params[key as keyof typeof ToolDirective.params]
}
}
// Create the object with the tool name and sorted parameters
const toolObject = {
- name: toolUse.name,
+ name: ToolDirective.name,
parameters: sortedParams,
}
diff --git a/src/core/tools/__tests__/ToolRepetitionDetector.spec.ts b/src/core/tools/__tests__/ToolRepetitionDetector.spec.ts
index 8dd5cd562e..e32e4223e8 100644
--- a/src/core/tools/__tests__/ToolRepetitionDetector.spec.ts
+++ b/src/core/tools/__tests__/ToolRepetitionDetector.spec.ts
@@ -3,9 +3,8 @@
import { vitest, describe, it, expect } from "vitest"
import type { ToolName } from "@roo-code/types"
-import type { ToolUse } from "../../../shared/tools"
-
import { ToolRepetitionDetector } from "../ToolRepetitionDetector"
+import { ToolDirective } from "../../message-parsing/directives"
vitest.mock("../../../i18n", () => ({
t: vitest.fn((key, options) => {
@@ -17,7 +16,7 @@ vitest.mock("../../../i18n", () => ({
}),
}))
-function createToolUse(name: string, displayName?: string, params: Record = {}): ToolUse {
+function createToolDirective(name: string, displayName?: string, params: Record = {}): ToolDirective {
return {
type: "tool_use",
name: (displayName || name) as ToolName,
@@ -34,15 +33,15 @@ describe("ToolRepetitionDetector", () => {
// We'll verify this through behavior in subsequent tests
// First call (counter = 1)
- const result1 = detector.check(createToolUse("test", "test-tool"))
+ const result1 = detector.check(createToolDirective("test", "test-tool"))
expect(result1.allowExecution).toBe(true)
// Second identical call (counter = 2)
- const result2 = detector.check(createToolUse("test", "test-tool"))
+ const result2 = detector.check(createToolDirective("test", "test-tool"))
expect(result2.allowExecution).toBe(true)
// Third identical call (counter = 3) reaches the default limit
- const result3 = detector.check(createToolUse("test", "test-tool"))
+ const result3 = detector.check(createToolDirective("test", "test-tool"))
expect(result3.allowExecution).toBe(false)
})
@@ -51,11 +50,11 @@ describe("ToolRepetitionDetector", () => {
const detector = new ToolRepetitionDetector(customLimit)
// First call (counter = 1)
- const result1 = detector.check(createToolUse("test", "test-tool"))
+ const result1 = detector.check(createToolDirective("test", "test-tool"))
expect(result1.allowExecution).toBe(true)
// Second identical call (counter = 2) reaches the custom limit
- const result2 = detector.check(createToolUse("test", "test-tool"))
+ const result2 = detector.check(createToolDirective("test", "test-tool"))
expect(result2.allowExecution).toBe(false)
})
})
@@ -65,15 +64,15 @@ describe("ToolRepetitionDetector", () => {
it("should allow execution for different tool calls", () => {
const detector = new ToolRepetitionDetector()
- const result1 = detector.check(createToolUse("first", "first-tool"))
+ const result1 = detector.check(createToolDirective("first", "first-tool"))
expect(result1.allowExecution).toBe(true)
expect(result1.askUser).toBeUndefined()
- const result2 = detector.check(createToolUse("second", "second-tool"))
+ const result2 = detector.check(createToolDirective("second", "second-tool"))
expect(result2.allowExecution).toBe(true)
expect(result2.askUser).toBeUndefined()
- const result3 = detector.check(createToolUse("third", "third-tool"))
+ const result3 = detector.check(createToolDirective("third", "third-tool"))
expect(result3.allowExecution).toBe(true)
expect(result3.askUser).toBeUndefined()
})
@@ -82,13 +81,13 @@ describe("ToolRepetitionDetector", () => {
const detector = new ToolRepetitionDetector(2)
// First call
- detector.check(createToolUse("same", "same-tool"))
+ detector.check(createToolDirective("same", "same-tool"))
// Second identical call would reach limit of 2, but we'll make a different call
- detector.check(createToolUse("different", "different-tool"))
+ detector.check(createToolDirective("different", "different-tool"))
// Back to the first tool - should be allowed since counter was reset
- const result = detector.check(createToolUse("same", "same-tool"))
+ const result = detector.check(createToolDirective("same", "same-tool"))
expect(result.allowExecution).toBe(true)
})
})
@@ -99,15 +98,15 @@ describe("ToolRepetitionDetector", () => {
const detector = new ToolRepetitionDetector(3)
// First call (counter = 1)
- const result1 = detector.check(createToolUse("repeat", "repeat-tool"))
+ const result1 = detector.check(createToolDirective("repeat", "repeat-tool"))
expect(result1.allowExecution).toBe(true)
// Second identical call (counter = 2)
- const result2 = detector.check(createToolUse("repeat", "repeat-tool"))
+ const result2 = detector.check(createToolDirective("repeat", "repeat-tool"))
expect(result2.allowExecution).toBe(true)
// Third identical call (counter = 3) reaches limit
- const result3 = detector.check(createToolUse("repeat", "repeat-tool"))
+ const result3 = detector.check(createToolDirective("repeat", "repeat-tool"))
expect(result3.allowExecution).toBe(false)
})
})
@@ -118,13 +117,13 @@ describe("ToolRepetitionDetector", () => {
const detector = new ToolRepetitionDetector(3)
// First call (counter = 1)
- detector.check(createToolUse("repeat", "repeat-tool"))
+ detector.check(createToolDirective("repeat", "repeat-tool"))
// Second identical call (counter = 2)
- detector.check(createToolUse("repeat", "repeat-tool"))
+ detector.check(createToolDirective("repeat", "repeat-tool"))
// Third identical call (counter = 3) - should reach limit
- const result = detector.check(createToolUse("repeat", "repeat-tool"))
+ const result = detector.check(createToolDirective("repeat", "repeat-tool"))
expect(result.allowExecution).toBe(false)
expect(result.askUser).toBeDefined()
@@ -136,12 +135,12 @@ describe("ToolRepetitionDetector", () => {
const detector = new ToolRepetitionDetector(2)
// Reach the limit
- detector.check(createToolUse("repeat", "repeat-tool"))
- const limitResult = detector.check(createToolUse("repeat", "repeat-tool")) // This reaches limit
+ detector.check(createToolDirective("repeat", "repeat-tool"))
+ const limitResult = detector.check(createToolDirective("repeat", "repeat-tool")) // This reaches limit
expect(limitResult.allowExecution).toBe(false)
// Use a new tool call - should be allowed since state was reset
- const result = detector.check(createToolUse("new", "new-tool"))
+ const result = detector.check(createToolDirective("new", "new-tool"))
expect(result.allowExecution).toBe(true)
})
})
@@ -152,12 +151,12 @@ describe("ToolRepetitionDetector", () => {
const detector = new ToolRepetitionDetector(2)
// Reach the limit with a specific tool
- detector.check(createToolUse("problem", "problem-tool"))
- const limitResult = detector.check(createToolUse("problem", "problem-tool")) // This reaches limit
+ detector.check(createToolDirective("problem", "problem-tool"))
+ const limitResult = detector.check(createToolDirective("problem", "problem-tool")) // This reaches limit
expect(limitResult.allowExecution).toBe(false)
// The same tool that previously caused problems should now be allowed
- const result = detector.check(createToolUse("problem", "problem-tool"))
+ const result = detector.check(createToolDirective("problem", "problem-tool"))
expect(result.allowExecution).toBe(true)
})
@@ -165,15 +164,15 @@ describe("ToolRepetitionDetector", () => {
const detector = new ToolRepetitionDetector(2)
// Reach the limit
- detector.check(createToolUse("repeat", "repeat-tool"))
- const limitResult = detector.check(createToolUse("repeat", "repeat-tool")) // This reaches limit
+ detector.check(createToolDirective("repeat", "repeat-tool"))
+ const limitResult = detector.check(createToolDirective("repeat", "repeat-tool")) // This reaches limit
expect(limitResult.allowExecution).toBe(false)
// First call after reset
- detector.check(createToolUse("repeat", "repeat-tool"))
+ detector.check(createToolDirective("repeat", "repeat-tool"))
// Second identical call (counter = 2) should reach limit again
- const result = detector.check(createToolUse("repeat", "repeat-tool"))
+ const result = detector.check(createToolDirective("repeat", "repeat-tool"))
expect(result.allowExecution).toBe(false)
expect(result.askUser).toBeDefined()
})
@@ -186,8 +185,8 @@ describe("ToolRepetitionDetector", () => {
const toolName = "special-tool-name"
// Reach the limit
- detector.check(createToolUse("test", toolName))
- const result = detector.check(createToolUse("test", toolName))
+ detector.check(createToolDirective("test", toolName))
+ const result = detector.check(createToolDirective("test", toolName))
expect(result.allowExecution).toBe(false)
expect(result.askUser?.messageDetail).toContain(toolName)
@@ -201,8 +200,8 @@ describe("ToolRepetitionDetector", () => {
// Create an empty tool call - a tool with no parameters
// Use the empty tool directly in the check calls
- detector.check(createToolUse("empty-tool", "empty-tool"))
- const result = detector.check(createToolUse("empty-tool"))
+ detector.check(createToolDirective("empty-tool", "empty-tool"))
+ const result = detector.check(createToolDirective("empty-tool"))
expect(result.allowExecution).toBe(false)
expect(result.askUser).toBeDefined()
@@ -212,28 +211,28 @@ describe("ToolRepetitionDetector", () => {
const detector = new ToolRepetitionDetector(2)
// First, call with tool-name-1 twice to set up the counter
- const toolUse1 = createToolUse("tool-name-1", "tool-name-1", { param: "value" })
- detector.check(toolUse1)
+ const ToolDirective1 = createToolDirective("tool-name-1", "tool-name-1", { param: "value" })
+ detector.check(ToolDirective1)
- // Create a tool that will serialize to the same JSON as toolUse1
- // We need to mock the serializeToolUse method to return the same value
- const toolUse2 = createToolUse("tool-name-2", "tool-name-2", { param: "value" })
+ // Create a tool that will serialize to the same JSON as ToolDirective1
+ // We need to mock the serializeToolDirective method to return the same value
+ const ToolDirective2 = createToolDirective("tool-name-2", "tool-name-2", { param: "value" })
// Override the private method to force identical serialization
- const originalSerialize = (detector as any).serializeToolUse
- ;(detector as any).serializeToolUse = (tool: ToolUse) => {
+ const originalSerialize = (detector as any).serializeToolDirective
+ ;(detector as any).serializeToolDirective = (tool: ToolDirective) => {
// Use string comparison for the name since it's technically an enum
if (String(tool.name) === "tool-name-2") {
- return (detector as any).serializeToolUse(toolUse1) // Return the same JSON as toolUse1
+ return (detector as any).serializeToolDirective(ToolDirective1) // Return the same JSON as ToolDirective1
}
return originalSerialize(tool)
}
// This should detect as a repetition now
- const result = detector.check(toolUse2)
+ const result = detector.check(ToolDirective2)
// Restore the original method
- ;(detector as any).serializeToolUse = originalSerialize
+ ;(detector as any).serializeToolDirective = originalSerialize
// Since we're directly manipulating the internal state for testing,
// we still expect it to consider this a repetition
@@ -245,14 +244,14 @@ describe("ToolRepetitionDetector", () => {
const detector = new ToolRepetitionDetector(2)
// First call with parameters in one order
- const toolUse1 = createToolUse("same-tool", "same-tool", { a: "1", b: "2", c: "3" })
- detector.check(toolUse1)
+ const ToolDirective1 = createToolDirective("same-tool", "same-tool", { a: "1", b: "2", c: "3" })
+ detector.check(ToolDirective1)
// Create tool with same parameters but in different order
- const toolUse2 = createToolUse("same-tool", "same-tool", { c: "3", a: "1", b: "2" })
+ const ToolDirective2 = createToolDirective("same-tool", "same-tool", { c: "3", a: "1", b: "2" })
// This should still detect as a repetition due to canonical JSON with sorted keys
- const result = detector.check(toolUse2)
+ const result = detector.check(ToolDirective2)
// Since parameters are sorted alphabetically in the serialized JSON,
// these should be considered identical
@@ -267,7 +266,7 @@ describe("ToolRepetitionDetector", () => {
const detector = new ToolRepetitionDetector(1)
// First call (counter = 1) should be blocked
- const result = detector.check(createToolUse("tool", "tool-name"))
+ const result = detector.check(createToolDirective("tool", "tool-name"))
expect(result.allowExecution).toBe(false)
expect(result.askUser).toBeDefined()
@@ -277,11 +276,11 @@ describe("ToolRepetitionDetector", () => {
const detector = new ToolRepetitionDetector(2)
// First call (counter = 1)
- const result1 = detector.check(createToolUse("tool", "tool-name"))
+ const result1 = detector.check(createToolDirective("tool", "tool-name"))
expect(result1.allowExecution).toBe(true)
// Second call (counter = 2) should be blocked
- const result2 = detector.check(createToolUse("tool", "tool-name"))
+ const result2 = detector.check(createToolDirective("tool", "tool-name"))
expect(result2.allowExecution).toBe(false)
expect(result2.askUser).toBeDefined()
})
@@ -290,15 +289,15 @@ describe("ToolRepetitionDetector", () => {
const detector = new ToolRepetitionDetector(3)
// First call (counter = 1)
- const result1 = detector.check(createToolUse("tool", "tool-name"))
+ const result1 = detector.check(createToolDirective("tool", "tool-name"))
expect(result1.allowExecution).toBe(true)
// Second call (counter = 2)
- const result2 = detector.check(createToolUse("tool", "tool-name"))
+ const result2 = detector.check(createToolDirective("tool", "tool-name"))
expect(result2.allowExecution).toBe(true)
// Third call (counter = 3) should be blocked
- const result3 = detector.check(createToolUse("tool", "tool-name"))
+ const result3 = detector.check(createToolDirective("tool", "tool-name"))
expect(result3.allowExecution).toBe(false)
expect(result3.askUser).toBeDefined()
})
diff --git a/src/core/tools/__tests__/executeCommandTool.spec.ts b/src/core/tools/__tests__/executeCommandTool.spec.ts
index 042fc263cf..8eb374299b 100644
--- a/src/core/tools/__tests__/executeCommandTool.spec.ts
+++ b/src/core/tools/__tests__/executeCommandTool.spec.ts
@@ -6,7 +6,7 @@ import type { ToolUsage } from "@roo-code/types"
import { Task } from "../../task/Task"
import { formatResponse } from "../../prompts/responses"
-import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../../shared/tools"
import { unescapeHtmlEntities } from "../../../utils/text-normalization"
// Mock dependencies
@@ -27,6 +27,7 @@ vitest.mock("../executeCommandTool")
// Import after mocking
import { executeCommandTool } from "../executeCommandTool"
+import { ToolDirective } from "../../message-parsing/directives"
// Now manually restore and mock the functions
beforeEach(() => {
@@ -79,7 +80,7 @@ describe("executeCommandTool", () => {
let mockHandleError: any
let mockPushToolResult: any
let mockRemoveClosingTag: any
- let mockToolUse: ToolUse
+ let mockToolDirective: ToolDirective
beforeEach(() => {
// Reset mocks
@@ -106,7 +107,7 @@ describe("executeCommandTool", () => {
mockRemoveClosingTag = vitest.fn().mockReturnValue("command")
// Create a mock tool use object
- mockToolUse = {
+ mockToolDirective = {
type: "tool_use",
name: "execute_command",
params: {
@@ -150,12 +151,12 @@ describe("executeCommandTool", () => {
describe("Basic functionality", () => {
it("should execute a command normally", async () => {
// Setup
- mockToolUse.params.command = "echo test"
+ mockToolDirective.params.command = "echo test"
// Execute
await executeCommandTool(
mockCline as unknown as Task,
- mockToolUse,
+ mockToolDirective,
mockAskApproval as unknown as AskApproval,
mockHandleError as unknown as HandleError,
mockPushToolResult as unknown as PushToolResult,
@@ -170,13 +171,13 @@ describe("executeCommandTool", () => {
it("should pass along custom working directory if provided", async () => {
// Setup
- mockToolUse.params.command = "echo test"
- mockToolUse.params.cwd = "/custom/path"
+ mockToolDirective.params.command = "echo test"
+ mockToolDirective.params.cwd = "/custom/path"
// Execute
await executeCommandTool(
mockCline as unknown as Task,
- mockToolUse,
+ mockToolDirective,
mockAskApproval as unknown as AskApproval,
mockHandleError as unknown as HandleError,
mockPushToolResult as unknown as PushToolResult,
@@ -194,12 +195,12 @@ describe("executeCommandTool", () => {
describe("Error handling", () => {
it("should handle missing command parameter", async () => {
// Setup
- mockToolUse.params.command = undefined
+ mockToolDirective.params.command = undefined
// Execute
await executeCommandTool(
mockCline as unknown as Task,
- mockToolUse,
+ mockToolDirective,
mockAskApproval as unknown as AskApproval,
mockHandleError as unknown as HandleError,
mockPushToolResult as unknown as PushToolResult,
@@ -216,13 +217,13 @@ describe("executeCommandTool", () => {
it("should handle command rejection", async () => {
// Setup
- mockToolUse.params.command = "echo test"
+ mockToolDirective.params.command = "echo test"
mockAskApproval.mockResolvedValue(false)
// Execute
await executeCommandTool(
mockCline as unknown as Task,
- mockToolUse,
+ mockToolDirective,
mockAskApproval as unknown as AskApproval,
mockHandleError as unknown as HandleError,
mockPushToolResult as unknown as PushToolResult,
@@ -237,7 +238,7 @@ describe("executeCommandTool", () => {
it("should handle rooignore validation failures", async () => {
// Setup
- mockToolUse.params.command = "cat .env"
+ mockToolDirective.params.command = "cat .env"
// Override the validateCommand mock to return a filename
const validateCommandMock = vitest.fn().mockReturnValue(".env")
mockCline.rooIgnoreController = {
@@ -251,7 +252,7 @@ describe("executeCommandTool", () => {
// Execute
await executeCommandTool(
mockCline as unknown as Task,
- mockToolUse,
+ mockToolDirective,
mockAskApproval as unknown as AskApproval,
mockHandleError as unknown as HandleError,
mockPushToolResult as unknown as PushToolResult,
diff --git a/src/core/tools/__tests__/newTaskTool.test.ts b/src/core/tools/__tests__/newTaskTool.test.ts
index 1a9e497df3..fa07b6cb72 100644
--- a/src/core/tools/__tests__/newTaskTool.test.ts
+++ b/src/core/tools/__tests__/newTaskTool.test.ts
@@ -52,7 +52,7 @@ jest.mock("../../prompts/responses", () => ({
// Import the function to test AFTER mocks are set up
import { newTaskTool } from "../newTaskTool"
-import type { ToolUse } from "../../../shared/tools"
+import { ToolDirective } from "../../message-parsing/directives"
describe("newTaskTool", () => {
beforeEach(() => {
@@ -65,7 +65,7 @@ describe("newTaskTool", () => {
})
it("should correctly un-escape \\\\@ to \\@ in the message passed to the new task", async () => {
- const block: ToolUse = {
+ const block: ToolDirective = {
type: "tool_use", // Add required 'type' property
name: "new_task", // Correct property name
params: {
@@ -102,7 +102,7 @@ describe("newTaskTool", () => {
})
it("should not un-escape single escaped \@", async () => {
- const block: ToolUse = {
+ const block: ToolDirective = {
type: "tool_use", // Add required 'type' property
name: "new_task", // Correct property name
params: {
@@ -129,7 +129,7 @@ describe("newTaskTool", () => {
})
it("should not un-escape non-escaped @", async () => {
- const block: ToolUse = {
+ const block: ToolDirective = {
type: "tool_use", // Add required 'type' property
name: "new_task", // Correct property name
params: {
@@ -156,7 +156,7 @@ describe("newTaskTool", () => {
})
it("should handle mixed escaping scenarios", async () => {
- const block: ToolUse = {
+ const block: ToolDirective = {
type: "tool_use", // Add required 'type' property
name: "new_task", // Correct property name
params: {
diff --git a/src/core/tools/__tests__/readFileTool.test.ts b/src/core/tools/__tests__/readFileTool.test.ts
index 3ed5cbe3f1..1a1b2da0a8 100644
--- a/src/core/tools/__tests__/readFileTool.test.ts
+++ b/src/core/tools/__tests__/readFileTool.test.ts
@@ -7,7 +7,7 @@ import { readLines } from "../../../integrations/misc/read-lines"
import { extractTextFromFile } from "../../../integrations/misc/extract-text"
import { parseSourceCodeDefinitionsForFile } from "../../../services/tree-sitter"
import { isBinaryFile } from "isbinaryfile"
-import { ReadFileToolUse, ToolParamName, ToolResponse } from "../../../shared/tools"
+import { ReadFileToolDirective, ToolParamName, ToolResponse } from "../../../shared/tools"
import { readFileTool } from "../readFileTool"
import { formatResponse } from "../../prompts/responses"
@@ -144,7 +144,7 @@ describe("read_file tool with maxReadFileLine setting", () => {
* Helper function to execute the read file tool with different maxReadFileLine settings
*/
async function executeReadFileTool(
- params: Partial = {},
+ params: Partial = {},
options: {
maxReadFileLine?: number
totalLines?: number
@@ -172,7 +172,7 @@ describe("read_file tool with maxReadFileLine setting", () => {
argsContent += ``
// Create a tool use object
- const toolUse: ReadFileToolUse = {
+ const toolUse: ReadFileToolDirective = {
type: "tool_use",
name: "read_file",
params: { args: argsContent, ...params },
@@ -484,7 +484,7 @@ describe("read_file tool XML output structure", () => {
argsContent += ``
// Create a tool use object
- const toolUse: ReadFileToolUse = {
+ const toolUse: ReadFileToolDirective = {
type: "tool_use",
name: "read_file",
params: { args: argsContent, ...params },
@@ -898,7 +898,7 @@ describe("read_file tool XML output structure", () => {
it("should include error tag for invalid path", async () => {
// Setup - missing path parameter
- const toolUse: ReadFileToolUse = {
+ const toolUse: ReadFileToolDirective = {
type: "tool_use",
name: "read_file",
params: {},
@@ -1167,7 +1167,7 @@ describe("read_file tool XML output structure", () => {
const mockRemoveClosingTag = jest.fn((tag, content) => content)
// Create a tool use object directly
- const toolUse: ReadFileToolUse = {
+ const toolUse: ReadFileToolDirective = {
type: "tool_use",
name: "read_file",
params: {
@@ -1207,7 +1207,7 @@ describe("read_file tool XML output structure", () => {
const mockRemoveClosingTag = jest.fn((tag, content) => content)
// Create a tool use object directly
- const toolUse: ReadFileToolUse = {
+ const toolUse: ReadFileToolDirective = {
type: "tool_use",
name: "read_file",
params: {
diff --git a/src/core/tools/__tests__/useMcpToolTool.test.ts b/src/core/tools/__tests__/useMcpToolTool.test.ts
index 24fa2540c0..dba754d8db 100644
--- a/src/core/tools/__tests__/useMcpToolTool.test.ts
+++ b/src/core/tools/__tests__/useMcpToolTool.test.ts
@@ -1,7 +1,7 @@
import { useMcpToolTool } from "../useMcpToolTool"
import { Task } from "../../task/Task"
-import { ToolUse } from "../../../shared/tools"
import { formatResponse } from "../../prompts/responses"
+import { ToolDirective } from "../../message-parsing/directives"
// Mock dependencies
jest.mock("../../prompts/responses", () => ({
@@ -57,7 +57,7 @@ describe("useMcpToolTool", () => {
describe("parameter validation", () => {
it("should handle missing server_name", async () => {
- const block: ToolUse = {
+ const block: ToolDirective = {
type: "tool_use",
name: "use_mcp_tool",
params: {
@@ -85,7 +85,7 @@ describe("useMcpToolTool", () => {
})
it("should handle missing tool_name", async () => {
- const block: ToolUse = {
+ const block: ToolDirective = {
type: "tool_use",
name: "use_mcp_tool",
params: {
@@ -113,7 +113,7 @@ describe("useMcpToolTool", () => {
})
it("should handle invalid JSON arguments", async () => {
- const block: ToolUse = {
+ const block: ToolDirective = {
type: "tool_use",
name: "use_mcp_tool",
params: {
@@ -142,7 +142,7 @@ describe("useMcpToolTool", () => {
describe("partial requests", () => {
it("should handle partial requests", async () => {
- const block: ToolUse = {
+ const block: ToolDirective = {
type: "tool_use",
name: "use_mcp_tool",
params: {
@@ -170,7 +170,7 @@ describe("useMcpToolTool", () => {
describe("successful execution", () => {
it("should execute tool successfully with valid parameters", async () => {
- const block: ToolUse = {
+ const block: ToolDirective = {
type: "tool_use",
name: "use_mcp_tool",
params: {
@@ -212,7 +212,7 @@ describe("useMcpToolTool", () => {
})
it("should handle user rejection", async () => {
- const block: ToolUse = {
+ const block: ToolDirective = {
type: "tool_use",
name: "use_mcp_tool",
params: {
@@ -241,7 +241,7 @@ describe("useMcpToolTool", () => {
describe("error handling", () => {
it("should handle unexpected errors", async () => {
- const block: ToolUse = {
+ const block: ToolDirective = {
type: "tool_use",
name: "use_mcp_tool",
params: {
diff --git a/src/core/tools/__tests__/writeToFileTool.test.ts b/src/core/tools/__tests__/writeToFileTool.test.ts
index e0789f766c..a8b942bb95 100644
--- a/src/core/tools/__tests__/writeToFileTool.test.ts
+++ b/src/core/tools/__tests__/writeToFileTool.test.ts
@@ -6,8 +6,9 @@ import { isPathOutsideWorkspace } from "../../../utils/pathUtils"
import { getReadablePath } from "../../../utils/path"
import { unescapeHtmlEntities } from "../../../utils/text-normalization"
import { everyLineHasLineNumbers, stripLineNumbers } from "../../../integrations/misc/extract-text"
-import { ToolUse, ToolResponse } from "../../../shared/tools"
+import { ToolResponse } from "../../../shared/tools"
import { writeToFileTool } from "../writeToFileTool"
+import { ToolDirective } from "../../message-parsing/directives"
jest.mock("path", () => {
const originalPath = jest.requireActual("path")
@@ -183,7 +184,7 @@ describe("writeToFileTool", () => {
* Helper function to execute the write file tool with different parameters
*/
async function executeWriteFileTool(
- params: Partial = {},
+ params: Partial = {},
options: {
fileExists?: boolean
isPartial?: boolean
@@ -199,7 +200,7 @@ describe("writeToFileTool", () => {
mockCline.rooIgnoreController.validateAccess.mockReturnValue(accessAllowed)
// Create a tool use object
- const toolUse: ToolUse = {
+ const ToolDirective: ToolDirective = {
type: "tool_use",
name: "write_to_file",
params: {
@@ -213,7 +214,7 @@ describe("writeToFileTool", () => {
await writeToFileTool(
mockCline,
- toolUse,
+ ToolDirective,
mockAskApproval,
mockHandleError,
(result: ToolResponse) => {
diff --git a/src/core/tools/accessMcpResourceTool.ts b/src/core/tools/accessMcpResourceTool.ts
index 22b1aba909..198d05948a 100644
--- a/src/core/tools/accessMcpResourceTool.ts
+++ b/src/core/tools/accessMcpResourceTool.ts
@@ -1,11 +1,12 @@
import { ClineAskUseMcpServer } from "../../shared/ExtensionMessage"
-import { ToolUse, RemoveClosingTag, AskApproval, HandleError, PushToolResult } from "../../shared/tools"
+import { RemoveClosingTag, AskApproval, HandleError, PushToolResult } from "../../shared/tools"
import { Task } from "../task/Task"
import { formatResponse } from "../prompts/responses"
+import { ToolDirective } from "../message-parsing/directives"
export async function accessMcpResourceTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/applyDiffTool.ts b/src/core/tools/applyDiffTool.ts
index d4f7fd883f..93f00ee1b9 100644
--- a/src/core/tools/applyDiffTool.ts
+++ b/src/core/tools/applyDiffTool.ts
@@ -6,15 +6,16 @@ import { TelemetryService } from "@roo-code/telemetry"
import { ClineSayTool } from "../../shared/ExtensionMessage"
import { getReadablePath } from "../../utils/path"
import { Task } from "../task/Task"
-import { ToolUse, RemoveClosingTag, AskApproval, HandleError, PushToolResult } from "../../shared/tools"
+import { 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 { ToolDirective } from "../message-parsing/directives"
export async function applyDiffToolLegacy(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/askFollowupQuestionTool.ts b/src/core/tools/askFollowupQuestionTool.ts
index d5adf6d4cd..f5eb10ac7f 100644
--- a/src/core/tools/askFollowupQuestionTool.ts
+++ b/src/core/tools/askFollowupQuestionTool.ts
@@ -1,11 +1,12 @@
import { Task } from "../task/Task"
-import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
import { formatResponse } from "../prompts/responses"
import { parseXml } from "../../utils/xml"
+import { ToolDirective } from "../message-parsing/directives"
export async function askFollowupQuestionTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/attemptCompletionTool.ts b/src/core/tools/attemptCompletionTool.ts
index d45ce0f65c..523380405f 100644
--- a/src/core/tools/attemptCompletionTool.ts
+++ b/src/core/tools/attemptCompletionTool.ts
@@ -5,7 +5,6 @@ import { TelemetryService } from "@roo-code/telemetry"
import { Task } from "../task/Task"
import {
ToolResponse,
- ToolUse,
AskApproval,
HandleError,
PushToolResult,
@@ -16,10 +15,11 @@ import {
import { formatResponse } from "../prompts/responses"
import { type ExecuteCommandOptions, executeCommand } from "./executeCommandTool"
import { EXPERIMENT_IDS, experiments, experimentDefault } from "../../shared/experiments"
+import { ToolDirective } from "../message-parsing/directives"
export async function attemptCompletionTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/browserActionTool.ts b/src/core/tools/browserActionTool.ts
index 13cb9b0ec2..d012183000 100644
--- a/src/core/tools/browserActionTool.ts
+++ b/src/core/tools/browserActionTool.ts
@@ -1,5 +1,5 @@
import { Task } from "../task/Task"
-import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
import {
BrowserAction,
BrowserActionResult,
@@ -7,10 +7,11 @@ import {
ClineSayBrowserAction,
} from "../../shared/ExtensionMessage"
import { formatResponse } from "../prompts/responses"
+import { ToolDirective } from "../message-parsing/directives"
export async function browserActionTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/codebaseSearchTool.ts b/src/core/tools/codebaseSearchTool.ts
index 236b066306..6e204948a3 100644
--- a/src/core/tools/codebaseSearchTool.ts
+++ b/src/core/tools/codebaseSearchTool.ts
@@ -5,12 +5,13 @@ import { CodeIndexManager } from "../../services/code-index/manager"
import { getWorkspacePath } from "../../utils/path"
import { formatResponse } from "../prompts/responses"
import { VectorStoreSearchResult } from "../../services/code-index/interfaces"
-import { AskApproval, HandleError, PushToolResult, RemoveClosingTag, ToolUse } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
+import { ToolDirective } from "../message-parsing/directives"
import path from "path"
export async function codebaseSearchTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/executeCommandTool.ts b/src/core/tools/executeCommandTool.ts
index e38d3c74f6..3dda433d11 100644
--- a/src/core/tools/executeCommandTool.ts
+++ b/src/core/tools/executeCommandTool.ts
@@ -8,18 +8,19 @@ import { TelemetryService } from "@roo-code/telemetry"
import { Task } from "../task/Task"
-import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag, ToolResponse } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag, ToolResponse } from "../../shared/tools"
import { formatResponse } from "../prompts/responses"
import { unescapeHtmlEntities } from "../../utils/text-normalization"
import { ExitCodeDetails, RooTerminalCallbacks, RooTerminalProcess } from "../../integrations/terminal/types"
import { TerminalRegistry } from "../../integrations/terminal/TerminalRegistry"
import { Terminal } from "../../integrations/terminal/Terminal"
+import { ToolDirective } from "../message-parsing/directives"
class ShellIntegrationError extends Error {}
export async function executeCommandTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/fetchInstructionsTool.ts b/src/core/tools/fetchInstructionsTool.ts
index 5325f98fbf..67a22ad460 100644
--- a/src/core/tools/fetchInstructionsTool.ts
+++ b/src/core/tools/fetchInstructionsTool.ts
@@ -2,11 +2,12 @@ import { Task } from "../task/Task"
import { fetchInstructions } from "../prompts/instructions/instructions"
import { ClineSayTool } from "../../shared/ExtensionMessage"
import { formatResponse } from "../prompts/responses"
-import { ToolUse, AskApproval, HandleError, PushToolResult } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult } from "../../shared/tools"
+import { ToolDirective } from "../message-parsing/directives"
export async function fetchInstructionsTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/insertContentTool.ts b/src/core/tools/insertContentTool.ts
index 0963bc78cc..f2a62998e6 100644
--- a/src/core/tools/insertContentTool.ts
+++ b/src/core/tools/insertContentTool.ts
@@ -4,16 +4,17 @@ import path from "path"
import { getReadablePath } from "../../utils/path"
import { Task } from "../task/Task"
-import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
import { formatResponse } from "../prompts/responses"
import { ClineSayTool } from "../../shared/ExtensionMessage"
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
import { fileExistsAtPath } from "../../utils/fs"
import { insertGroups } from "../diff/insert-groups"
+import { ToolDirective } from "../message-parsing/directives"
export async function insertContentTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/listCodeDefinitionNamesTool.ts b/src/core/tools/listCodeDefinitionNamesTool.ts
index f0cdde01eb..504c554b58 100644
--- a/src/core/tools/listCodeDefinitionNamesTool.ts
+++ b/src/core/tools/listCodeDefinitionNamesTool.ts
@@ -1,16 +1,17 @@
import path from "path"
import fs from "fs/promises"
-import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
import { Task } from "../task/Task"
import { ClineSayTool } from "../../shared/ExtensionMessage"
import { getReadablePath } from "../../utils/path"
import { parseSourceCodeForDefinitionsTopLevel, parseSourceCodeDefinitionsForFile } from "../../services/tree-sitter"
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
+import { ToolDirective } from "../message-parsing/directives"
export async function listCodeDefinitionNamesTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/listFilesTool.ts b/src/core/tools/listFilesTool.ts
index 6d40de711c..accd13869c 100644
--- a/src/core/tools/listFilesTool.ts
+++ b/src/core/tools/listFilesTool.ts
@@ -5,7 +5,8 @@ import { ClineSayTool } from "../../shared/ExtensionMessage"
import { formatResponse } from "../prompts/responses"
import { listFiles } from "../../services/glob/list-files"
import { getReadablePath } from "../../utils/path"
-import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
+import { ToolDirective } from "../message-parsing/directives"
/**
* Implements the list_files tool.
@@ -24,7 +25,7 @@ import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } f
export async function listFilesTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/multiApplyDiffTool.ts b/src/core/tools/multiApplyDiffTool.ts
index ba36cd3759..e507b94d47 100644
--- a/src/core/tools/multiApplyDiffTool.ts
+++ b/src/core/tools/multiApplyDiffTool.ts
@@ -6,7 +6,7 @@ import { TelemetryService } from "@roo-code/telemetry"
import { ClineSayTool } from "../../shared/ExtensionMessage"
import { getReadablePath } from "../../utils/path"
import { Task } from "../task/Task"
-import { ToolUse, RemoveClosingTag, AskApproval, HandleError, PushToolResult } from "../../shared/tools"
+import { RemoveClosingTag, AskApproval, HandleError, PushToolResult } from "../../shared/tools"
import { formatResponse } from "../prompts/responses"
import { fileExistsAtPath } from "../../utils/fs"
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
@@ -14,6 +14,7 @@ import { unescapeHtmlEntities } from "../../utils/text-normalization"
import { parseXml } from "../../utils/xml"
import { EXPERIMENT_IDS, experiments } from "../../shared/experiments"
import { applyDiffToolLegacy } from "./applyDiffTool"
+import { ToolDirective } from "../message-parsing/directives"
interface DiffOperation {
path: string
@@ -51,7 +52,7 @@ interface ParsedXmlResult {
export async function applyDiffTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
@@ -161,7 +162,6 @@ Expected structure:
Original error: ${errorMessage}`
throw new Error(detailedError)
}
-
} else if (legacyPath && typeof legacyDiffContent === "string") {
// Handle legacy parameters (old way)
usingLegacyParams = true
diff --git a/src/core/tools/newTaskTool.ts b/src/core/tools/newTaskTool.ts
index 25d5766d5d..f3eda79e95 100644
--- a/src/core/tools/newTaskTool.ts
+++ b/src/core/tools/newTaskTool.ts
@@ -1,13 +1,14 @@
import delay from "delay"
-import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
import { Task } from "../task/Task"
import { defaultModeSlug, getModeBySlug } from "../../shared/modes"
import { formatResponse } from "../prompts/responses"
+import { ToolDirective } from "../message-parsing/directives"
export async function newTaskTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
@@ -43,7 +44,7 @@ export async function newTaskTool(
cline.consecutiveMistakeCount = 0
// Un-escape one level of backslashes before '@' for hierarchical subtasks
-// Un-escape one level: \\@ -> \@ (removes one backslash for hierarchical subtasks)
+ // Un-escape one level: \\@ -> \@ (removes one backslash for hierarchical subtasks)
const unescapedMessage = message.replace(/\\\\@/g, "\\@")
// Verify the mode exists
diff --git a/src/core/tools/readFileTool.ts b/src/core/tools/readFileTool.ts
index e49ac43d7b..897c249d4c 100644
--- a/src/core/tools/readFileTool.ts
+++ b/src/core/tools/readFileTool.ts
@@ -5,7 +5,7 @@ import { Task } from "../task/Task"
import { ClineSayTool } from "../../shared/ExtensionMessage"
import { formatResponse } from "../prompts/responses"
import { t } from "../../i18n"
-import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
import { isPathOutsideWorkspace } from "../../utils/pathUtils"
import { getReadablePath } from "../../utils/path"
@@ -14,6 +14,7 @@ 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 { ToolDirective } from "../message-parsing/directives"
export function getReadFileToolDescription(blockName: string, blockParams: any): string {
// Handle both single path and multiple files via args
@@ -72,7 +73,7 @@ interface FileResult {
export async function readFileTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/searchAndReplaceTool.ts b/src/core/tools/searchAndReplaceTool.ts
index 58d246b133..4ec0865245 100644
--- a/src/core/tools/searchAndReplaceTool.ts
+++ b/src/core/tools/searchAndReplaceTool.ts
@@ -5,12 +5,13 @@ import delay from "delay"
// Internal imports
import { Task } from "../task/Task"
-import { AskApproval, HandleError, PushToolResult, RemoveClosingTag, ToolUse } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
import { formatResponse } from "../prompts/responses"
import { ClineSayTool } from "../../shared/ExtensionMessage"
import { getReadablePath } from "../../utils/path"
import { fileExistsAtPath } from "../../utils/fs"
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
+import { ToolDirective } from "../message-parsing/directives"
/**
* Tool for performing search and replace operations on files
@@ -62,7 +63,7 @@ async function validateParams(
*/
export async function searchAndReplaceTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/searchFilesTool.ts b/src/core/tools/searchFilesTool.ts
index 6528f20d54..dd6b0aa336 100644
--- a/src/core/tools/searchFilesTool.ts
+++ b/src/core/tools/searchFilesTool.ts
@@ -1,14 +1,15 @@
import path from "path"
import { Task } from "../task/Task"
-import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
import { ClineSayTool } from "../../shared/ExtensionMessage"
import { getReadablePath } from "../../utils/path"
import { regexSearchFiles } from "../../services/ripgrep"
+import { ToolDirective } from "../message-parsing/directives"
export async function searchFilesTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/switchModeTool.ts b/src/core/tools/switchModeTool.ts
index 8ce906b41f..63d8a17ab8 100644
--- a/src/core/tools/switchModeTool.ts
+++ b/src/core/tools/switchModeTool.ts
@@ -1,13 +1,14 @@
import delay from "delay"
import { Task } from "../task/Task"
-import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
import { formatResponse } from "../prompts/responses"
import { defaultModeSlug, getModeBySlug } from "../../shared/modes"
+import { ToolDirective } from "../message-parsing/directives"
export async function switchModeTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/useMcpToolTool.ts b/src/core/tools/useMcpToolTool.ts
index 30dff5ce4f..630299f5e2 100644
--- a/src/core/tools/useMcpToolTool.ts
+++ b/src/core/tools/useMcpToolTool.ts
@@ -1,9 +1,10 @@
import { Task } from "../task/Task"
-import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
import { formatResponse } from "../prompts/responses"
import { ClineAskUseMcpServer } from "../../shared/ExtensionMessage"
import { McpExecutionStatus } from "@roo-code/types"
import { t } from "../../i18n"
+import { ToolDirective } from "../message-parsing/directives"
interface McpToolParams {
server_name?: string
@@ -166,7 +167,7 @@ async function executeToolAndProcessResult(
export async function useMcpToolTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/core/tools/writeToFileTool.ts b/src/core/tools/writeToFileTool.ts
index f7543d6d8b..6f306e8b13 100644
--- a/src/core/tools/writeToFileTool.ts
+++ b/src/core/tools/writeToFileTool.ts
@@ -5,7 +5,7 @@ import * as vscode from "vscode"
import { Task } from "../task/Task"
import { ClineSayTool } from "../../shared/ExtensionMessage"
import { formatResponse } from "../prompts/responses"
-import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
+import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
import { fileExistsAtPath } from "../../utils/fs"
import { stripLineNumbers, everyLineHasLineNumbers } from "../../integrations/misc/extract-text"
@@ -13,10 +13,11 @@ import { getReadablePath } from "../../utils/path"
import { isPathOutsideWorkspace } from "../../utils/pathUtils"
import { detectCodeOmission } from "../../integrations/editor/detect-omission"
import { unescapeHtmlEntities } from "../../utils/text-normalization"
+import { ToolDirective } from "../message-parsing/directives"
export async function writeToFileTool(
cline: Task,
- block: ToolUse,
+ block: ToolDirective,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
diff --git a/src/shared/tools.ts b/src/shared/tools.ts
index 7525f85088..bea13447f6 100644
--- a/src/shared/tools.ts
+++ b/src/shared/tools.ts
@@ -1,6 +1,7 @@
import { Anthropic } from "@anthropic-ai/sdk"
import type { ClineAsk, ToolProgressStatus, ToolGroup, ToolName } from "@roo-code/types"
+import { ToolDirective } from "../core/message-parsing/directives/"
export type ToolResponse = string | Array
@@ -61,96 +62,88 @@ export const toolParamNames = [
export type ToolParamName = (typeof toolParamNames)[number]
-export interface ToolUse {
- type: "tool_use"
- name: ToolName
- // params is a partial record, allowing only some or none of the possible parameters to be used
- params: Partial>
- partial: boolean
-}
-
-export interface ExecuteCommandToolUse extends ToolUse {
+export interface ExecuteCommandToolDirective extends ToolDirective {
name: "execute_command"
// Pick, "command"> makes "command" required, but Partial<> makes it optional
params: Partial, "command" | "cwd">>
}
-export interface ReadFileToolUse extends ToolUse {
+export interface ReadFileToolDirective extends ToolDirective {
name: "read_file"
params: Partial, "args" | "path" | "start_line" | "end_line">>
}
-export interface FetchInstructionsToolUse extends ToolUse {
+export interface FetchInstructionsToolDirective extends ToolDirective {
name: "fetch_instructions"
params: Partial, "task">>
}
-export interface WriteToFileToolUse extends ToolUse {
+export interface WriteToFileToolDirective extends ToolDirective {
name: "write_to_file"
params: Partial, "path" | "content" | "line_count">>
}
-export interface InsertCodeBlockToolUse extends ToolUse {
+export interface InsertCodeBlockToolDirective extends ToolDirective {
name: "insert_content"
params: Partial, "path" | "line" | "content">>
}
-export interface CodebaseSearchToolUse extends ToolUse {
+export interface CodebaseSearchToolDirective extends ToolDirective {
name: "codebase_search"
params: Partial, "query" | "path">>
}
-export interface SearchFilesToolUse extends ToolUse {
+export interface SearchFilesToolDirective extends ToolDirective {
name: "search_files"
params: Partial, "path" | "regex" | "file_pattern">>
}
-export interface ListFilesToolUse extends ToolUse {
+export interface ListFilesToolDirective extends ToolDirective {
name: "list_files"
params: Partial, "path" | "recursive">>
}
-export interface ListCodeDefinitionNamesToolUse extends ToolUse {
+export interface ListCodeDefinitionNamesToolDirective extends ToolDirective {
name: "list_code_definition_names"
params: Partial, "path">>
}
-export interface BrowserActionToolUse extends ToolUse {
+export interface BrowserActionToolDirective extends ToolDirective {
name: "browser_action"
params: Partial, "action" | "url" | "coordinate" | "text" | "size">>
}
-export interface UseMcpToolToolUse extends ToolUse {
+export interface UseMcpToolToolDirective extends ToolDirective {
name: "use_mcp_tool"
params: Partial, "server_name" | "tool_name" | "arguments">>
}
-export interface AccessMcpResourceToolUse extends ToolUse {
+export interface AccessMcpResourceToolDirective extends ToolDirective {
name: "access_mcp_resource"
params: Partial, "server_name" | "uri">>
}
-export interface AskFollowupQuestionToolUse extends ToolUse {
+export interface AskFollowupQuestionToolDirective extends ToolDirective {
name: "ask_followup_question"
params: Partial, "question" | "follow_up">>
}
-export interface AttemptCompletionToolUse extends ToolUse {
+export interface AttemptCompletionToolDirective extends ToolDirective {
name: "attempt_completion"
params: Partial, "result" | "command">>
}
-export interface SwitchModeToolUse extends ToolUse {
+export interface SwitchModeToolDirective extends ToolDirective {
name: "switch_mode"
params: Partial, "mode_slug" | "reason">>
}
-export interface NewTaskToolUse extends ToolUse {
+export interface NewTaskToolDirective extends ToolDirective {
name: "new_task"
params: Partial, "mode" | "message">>
}
-export interface SearchAndReplaceToolUse extends ToolUse {
+export interface SearchAndReplaceToolDirective extends ToolDirective {
name: "search_and_replace"
params: Required, "path" | "search" | "replace">> &
Partial, "use_regex" | "ignore_case" | "start_line" | "end_line">>
@@ -270,5 +263,5 @@ export interface DiffStrategy {
endLine?: number,
): Promise
- getProgressStatus?(toolUse: ToolUse, result?: any): ToolProgressStatus
+ getProgressStatus?(ToolDirective: ToolDirective, result?: any): ToolProgressStatus
}