diff --git a/site/src/pages/AgentsPage/components/ChatElements/tools/Tool.tsx b/site/src/pages/AgentsPage/components/ChatElements/tools/Tool.tsx index 5934b2524f..4e8d6aa237 100644 --- a/site/src/pages/AgentsPage/components/ChatElements/tools/Tool.tsx +++ b/site/src/pages/AgentsPage/components/ChatElements/tools/Tool.tsx @@ -1016,7 +1016,7 @@ const StartWorkspaceRenderer: FC = ({ // Renderer lookup map for tool names and specialized renderers. // --------------------------------------------------------------------------- -const toolRenderers: Record> = { +export const toolRenderers: Record> = { execute: ExecuteRenderer, process_output: ProcessOutputRenderer, process_signal: ProcessSignalRenderer, diff --git a/site/src/pages/AgentsPage/components/ChatElements/tools/ToolIcon.tsx b/site/src/pages/AgentsPage/components/ChatElements/tools/ToolIcon.tsx index 913b8d0bc8..b74f26d148 100644 --- a/site/src/pages/AgentsPage/components/ChatElements/tools/ToolIcon.tsx +++ b/site/src/pages/AgentsPage/components/ChatElements/tools/ToolIcon.tsx @@ -5,6 +5,7 @@ import { FilePenLineIcon, FileTextIcon, LightbulbIcon, + type LucideIcon, MonitorIcon, PowerIcon, RouteIcon, @@ -22,6 +23,28 @@ import { } from "#/components/Tooltip/Tooltip"; import { cn } from "#/utils/cn"; +export const toolIcons: Partial> = { + execute: TerminalIcon, + process_output: TerminalIcon, + process_list: TerminalIcon, + process_signal: TerminalIcon, + read_file: FileTextIcon, + read_skill: FileTextIcon, + write_file: FilePenLineIcon, + edit_files: FilePenLineIcon, + list_templates: ServerIcon, + read_template: ServerIcon, + create_workspace: ServerIcon, + start_workspace: PowerIcon, + chat_summarized: BotIcon, + list_agents: BotIcon, + thinking: LightbulbIcon, + propose_plan: RouteIcon, + ask_user_question: BadgeQuestionMarkIcon, + advisor: CompassIcon, + computer: MonitorIcon, +}; + export const ToolIcon: React.FC<{ name: string; iconUrl?: string; @@ -73,39 +96,6 @@ export const ToolIcon: React.FC<{ return img; } - switch (name) { - case "execute": - case "process_output": - case "process_list": - case "process_signal": - return ; - case "read_file": - case "read_skill": - return ; - case "write_file": - case "edit_files": - return ; - case "list_templates": - case "read_template": - case "create_workspace": - return ; - case "start_workspace": - return ; - case "chat_summarized": - case "list_agents": - return ; - case "thinking": - return ; - case "propose_plan": - return ; - case "ask_user_question": - return ; - case "advisor": - return ; - case "computer": - return ; - - default: - return ; - } + const Icon = toolIcons[name] ?? WrenchIcon; + return ; }; diff --git a/site/src/pages/AgentsPage/components/ChatElements/tools/ToolLabel.tsx b/site/src/pages/AgentsPage/components/ChatElements/tools/ToolLabel.tsx index 4b465e7b03..bbe9b59daf 100644 --- a/site/src/pages/AgentsPage/components/ChatElements/tools/ToolLabel.tsx +++ b/site/src/pages/AgentsPage/components/ChatElements/tools/ToolLabel.tsx @@ -2,75 +2,86 @@ import type React from "react"; import { getPathBasename } from "../../../utils/path"; import { asRecord, asString, humanizeMCPToolName, parseArgs } from "./utils"; -export const ToolLabel: React.FC<{ +type ToolLabelProps = { name: string; args: unknown; result: unknown; mcpSlug?: string; -}> = ({ name, args, result, mcpSlug }) => { +}; + +const ProcessSignalLabel: React.FC = ({ args, result }) => { const parsed = parseArgs(args); const parsedResult = asRecord(result); + const signal = parsed ? asString(parsed.signal) : ""; + const processId = parsed ? asString(parsed.process_id) : ""; + const shortId = processId ? processId.slice(0, 8) : ""; + const suffix = shortId ? ` ${shortId}` : ""; + const isKill = signal === "kill"; + const isTerminate = signal === "terminate"; - switch (name) { - case "process_signal": { - const signal = parsed ? asString(parsed.signal) : ""; - const processId = parsed ? asString(parsed.process_id) : ""; - const shortId = processId ? processId.slice(0, 8) : ""; - const hasResult = result !== undefined && result !== null; - const success = parsedResult ? Boolean(parsedResult.success) : false; - if (hasResult && success) { - const verb = signal === "kill" ? "Killed" : "Terminated"; - return ( - - {verb} process{shortId ? ` ${shortId}` : ""} - - ); - } - if (hasResult && !success) { - const verb = - signal === "kill" - ? "kill" - : signal === "terminate" - ? "terminate" - : "signal"; - return ( - - Failed to {verb} process{shortId ? ` ${shortId}` : ""} - - ); - } - return ( - - {signal === "kill" - ? "Killing process…" - : signal === "terminate" - ? "Terminating process…" - : "Sending signal…"} - - ); - } - case "process_list": - return Listing processes; - case "attach_file": { - const attachedName = - (parsedResult ? asString(parsedResult.name) : "") || - (parsed ? asString(parsed.name) : "") || - (parsed ? getPathBasename(asString(parsed.path)) : "") || - "file"; - return ( - {`Attached ${attachedName}`} - ); - } - case "advisor": - return ( - - Advisor - - ); - - default: { - const displayName = mcpSlug ? humanizeMCPToolName(mcpSlug, name) : name; - return {displayName}; - } + const hasResult = result !== undefined && result !== null; + if (!hasResult) { + const inFlightVerb = isKill + ? "Killing process…" + : isTerminate + ? "Terminating process…" + : "Sending signal…"; + return {inFlightVerb}; } + + const success = parsedResult ? Boolean(parsedResult.success) : false; + if (success) { + const verb = isKill ? "Killed" : "Terminated"; + return ( + + {verb} process{suffix} + + ); + } + + const failedVerb = isKill ? "kill" : isTerminate ? "terminate" : "signal"; + return ( + + Failed to {failedVerb} process{suffix} + + ); +}; + +const AttachFileLabel: React.FC = ({ args, result }) => { + const parsed = parseArgs(args); + const parsedResult = asRecord(result); + const resultName = parsedResult ? asString(parsedResult.name) : ""; + const argName = parsed ? asString(parsed.name) : ""; + const argPath = parsed ? asString(parsed.path) : ""; + const attachedName = + resultName || argName || getPathBasename(argPath) || "file"; + return ( + {`Attached ${attachedName}`} + ); +}; + +export const genericToolLabels: Partial< + Record> +> = { + process_signal: ProcessSignalLabel, + process_list: () => ( + Listing processes + ), + attach_file: AttachFileLabel, + advisor: () => ( + + Advisor + + ), +}; + +export const ToolLabel: React.FC = (props) => { + const Label = genericToolLabels[props.name]; + if (Label) { + return