diff --git a/src/common/utils/tools/toolDefinitions.test.ts b/src/common/utils/tools/toolDefinitions.test.ts index f62bd049d8..9011bb01db 100644 --- a/src/common/utils/tools/toolDefinitions.test.ts +++ b/src/common/utils/tools/toolDefinitions.test.ts @@ -152,6 +152,37 @@ describe("TOOL_DEFINITIONS", () => { expect(parsed.success).toBe(false); }); + it("accepts bash tool calls using description (alias for display_name)", () => { + // DeepSeek v4 emits `description` instead of `display_name`; ensure it normalizes. + const parsed = TOOL_DEFINITIONS.bash.schema.safeParse({ + script: "ls", + timeout_secs: 60, + run_in_background: false, + description: "List files", + }); + + expect(parsed.success).toBe(true); + if (parsed.success) { + expect(parsed.data.display_name).toBe("List files"); + expect("description" in parsed.data).toBe(false); + } + }); + + it("prefers display_name when both display_name and description are provided", () => { + const parsed = TOOL_DEFINITIONS.bash.schema.safeParse({ + script: "ls", + timeout_secs: 60, + run_in_background: false, + display_name: "Real Name", + description: "Alias Name", + }); + + expect(parsed.success).toBe(true); + if (parsed.success) { + expect(parsed.data.display_name).toBe("Real Name"); + } + }); + const filePathAliasCases = [ { toolName: "file_read", diff --git a/src/common/utils/tools/toolDefinitions.ts b/src/common/utils/tools/toolDefinitions.ts index fe489597d5..e54f5b173b 100644 --- a/src/common/utils/tools/toolDefinitions.ts +++ b/src/common/utils/tools/toolDefinitions.ts @@ -852,16 +852,22 @@ export const TOOL_DEFINITIONS = { // Normalize to `script` so downstream code (tool runner + UI) stays consistent. if (typeof value !== "object" || value === null || Array.isArray(value)) return value; - const obj = value as Record; - if (typeof obj.script === "string") return value; + let obj = value as Record; - if (typeof obj.command === "string") { + if (typeof obj.script !== "string" && typeof obj.command === "string") { // Drop the legacy field to keep tool args canonical (and avoid confusing downstream consumers). const { command, ...rest } = obj as Record & { command: string }; - return { ...rest, script: command }; + obj = { ...rest, script: command }; } - return value; + // Compatibility: DeepSeek v4 emits `description` instead of `display_name`. + // Treat `description` as an undocumented alias so the call still validates. + if (typeof obj.display_name !== "string" && typeof obj.description === "string") { + const { description, ...rest } = obj as Record & { description: string }; + obj = { ...rest, display_name: description }; + } + + return obj; }, z.object({ script: z.string().describe("The bash script/command to execute"),