From b8f7f9e75c06cb79d78aa4bc9a3f05d72a8df367 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sat, 1 Aug 2026 09:32:26 -0700 Subject: [PATCH] FN-8701: standardize tool-call detail displays Standardize expandable tool-call payloads across dashboard chat and log surfaces. - Add a shared formatter and detail renderer that preserves complete browser-available arguments and results. - Use compact previews with wrapped, scrollable expanded payloads in chat, activity, and agent logs. - Cover formatting and rendering behavior with dashboard tests and document the display contract. Files changed: .changeset/fn-8701-tool-call-display.md | 7 ++ docs/dashboard-guide.md | 4 +- .../dashboard/app/components/AgentLogViewer.tsx | 12 +++- packages/dashboard/app/components/ChatView.css | 37 ++--------- .../app/components/StandardChatSurface.tsx | 56 +++++++--------- packages/dashboard/app/components/TaskChatTab.css | 17 ----- packages/dashboard/app/components/TaskChatTab.tsx | 31 +++++---- .../dashboard/app/components/ToolCallDetails.css | 52 +++++++++++++++ .../dashboard/app/components/ToolCallDetails.tsx | 77 ++++++++++++++++++++++ .../__tests__/AgentLogViewer.rendering.test.tsx | 8 +++ .../components/__tests__/ChatView.core.test.tsx | 24 +++++++ .../app/components/__tests__/TaskChatTab.test.tsx | 33 +++++++--- .../__tests__/TaskPlannerChatTab.test.tsx | 19 ++++++ 13 files changed, 275 insertions(+), 102 deletions(-) Fusion-Task-Id: FN-8701 Fusion-Task-Lineage: 86513f9a-31d4-45fb-97fe-1f0c73d0e5b3 Co-authored-by: Fusion (runfusion.ai) --- .changeset/fn-8701-tool-call-display.md | 7 ++ docs/dashboard-guide.md | 4 +- .../app/components/AgentLogViewer.tsx | 12 ++- .../dashboard/app/components/ChatView.css | 37 ++------- .../app/components/StandardChatSurface.tsx | 56 ++++++-------- .../dashboard/app/components/TaskChatTab.css | 17 ---- .../dashboard/app/components/TaskChatTab.tsx | 31 ++++---- .../app/components/ToolCallDetails.css | 52 +++++++++++++ .../app/components/ToolCallDetails.tsx | 77 +++++++++++++++++++ .../AgentLogViewer.rendering.test.tsx | 8 ++ .../__tests__/ChatView.core.test.tsx | 24 ++++++ .../components/__tests__/TaskChatTab.test.tsx | 33 ++++++-- .../__tests__/TaskPlannerChatTab.test.tsx | 19 +++++ 13 files changed, 275 insertions(+), 102 deletions(-) create mode 100644 .changeset/fn-8701-tool-call-display.md create mode 100644 packages/dashboard/app/components/ToolCallDetails.css create mode 100644 packages/dashboard/app/components/ToolCallDetails.tsx diff --git a/.changeset/fn-8701-tool-call-display.md b/.changeset/fn-8701-tool-call-display.md new file mode 100644 index 0000000000..0cea5b0774 --- /dev/null +++ b/.changeset/fn-8701-tool-call-display.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Show complete available tool-call details when expanding chats and logs. +category: fix +dev: Shares tool-call payload formatting across chat, Activity, and Agent Log Viewer. diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md index b88a6ac30f..47569e1999 100644 --- a/docs/dashboard-guide.md +++ b/docs/dashboard-guide.md @@ -707,7 +707,9 @@ Chat view provides project-scoped conversations with agents. - On desktop/tablet Direct chat, the thread header shows an estimated token count against the active model's known context window (for example `~12.3k / 200k`). It is hidden on mobile, narrow floating chat, rooms, and unknown-context-window models. - In narrow Chat containers (including phone-width full Chat, narrow Quick Chat popups, and right-dock Chat), message bubbles use the full content width for improved readability. On tablet-width main Chat containers, assistant/agent, streaming, and failure bubbles keep the wider 92% reading measure, while wide desktop Chat keeps the standard 75% bubble cap. -- Full Chat tool-call summaries now use a denser mobile layout: grouped and single-call collapsed rows keep icon + label + status on one line (Quick Chat-style scanability) while expanded details remain unchanged. +- Full Chat tool-call summaries now use a denser mobile layout: grouped and single-call collapsed rows keep icon + label + status on one line (Quick Chat-style scanability). +- Tool calls in direct, room, Quick, and Planner Chat plus task Activity and Agent Log Viewer keep collapsed previews compact, but opening their disclosure shows the complete arguments and result/output that reached the browser. Multiline output preserves line breaks; ordinary text wraps and exceptionally long paths or tokens scroll inside the detail instead of widening the page, including at the mobile breakpoint. This removes UI-created preview truncation only: intentional engine persistence, context, API, and tool-output budgets still determine what payload is available to display. + - Assistant question tool calls now render as a shared in-chat response card instead of a generic tool-call disclosure. The card recognizes provider-native question tools and Fusion's `fn_ask_question`, supports select, multi-select, text, and yes/no prompts, sends the formatted answer back into the same direct or room thread, and renders historical answered questions read-only. - The desktop Chat view toggle and mobile Chat tab now show an unread-response indicator when a live assistant reply arrives for a visible direct or room chat after you leave Chat; opening Chat clears it immediately. Task-detail planner Chat replies stay task-local and do not light up the global Chat unread indicator while those sessions are hidden from the common Chat feed. diff --git a/packages/dashboard/app/components/AgentLogViewer.tsx b/packages/dashboard/app/components/AgentLogViewer.tsx index b57e435fc0..8bd08adf3f 100644 --- a/packages/dashboard/app/components/AgentLogViewer.tsx +++ b/packages/dashboard/app/components/AgentLogViewer.tsx @@ -13,6 +13,7 @@ import { Maximize2, Minimize2, Loader2, ChevronDown, ChevronRight } from "lucide import "./AgentLogViewer.css"; import { linkifyFilePaths, linkifyReactChildren } from "../utils/filePathLinkify"; import { getRelativeTimeBucket } from "../utils/relativeTimeAgo"; +import { ToolCallDetails } from "./ToolCallDetails"; const MARKDOWN_TOGGLE_STORAGE_KEY = "fn-agent-log-markdown"; const TOOL_OUTPUT_TOGGLE_STORAGE_KEY = "fn-agent-log-tool-output"; @@ -173,7 +174,7 @@ interface CollapsibleToolDetailProps { type?: "tool" | "tool_result" | "tool_error"; } -function CollapsibleToolDetail({ detail }: CollapsibleToolDetailProps): ReactElement { +function CollapsibleToolDetail({ detail, type = "tool_result" }: CollapsibleToolDetailProps): ReactElement { const { t } = useTranslation("app"); const [expanded, setExpanded] = useState(false); const contentId = useId(); @@ -200,7 +201,14 @@ function CollapsibleToolDetail({ detail }: CollapsibleToolDetailProps): ReactEle className={expanded ? "agent-log-tool-detail-content" : "agent-log-tool-detail-content agent-log-tool-detail-content--collapsed"} data-testid="tool-detail-content" > -
{linkifyFilePaths(detail)}
+ ); diff --git a/packages/dashboard/app/components/ChatView.css b/packages/dashboard/app/components/ChatView.css index a236e52ec4..fd6099ddbb 100644 --- a/packages/dashboard/app/components/ChatView.css +++ b/packages/dashboard/app/components/ChatView.css @@ -1679,7 +1679,8 @@ User messages in direct (model-loop) chat carry a compact edit affordance inline background: color-mix(in srgb, var(--surface) 35%, transparent); } -.chat-tool-call summary { +.chat-tool-call summary, +.chat-tool-call-summary { display: flex; align-items: center; gap: var(--space-xs); @@ -1749,32 +1750,13 @@ User messages in direct (model-loop) chat carry a compact edit affordance inline gap: var(--space-xs); } -.chat-tool-call-row { - display: grid; - grid-template-columns: auto 1fr; - gap: var(--space-xs); - align-items: start; -} - -.chat-tool-call-label { - color: var(--text-muted); - text-transform: uppercase; - letter-spacing: 0.04em; - font-size: 0.6875rem; -} - -.chat-tool-call-value { - color: var(--text); - word-break: break-word; - white-space: pre-wrap; -} - .chat-tool-call--running .chat-tool-call-status-dot { background: var(--color-info); animation: tool-call-pulse var(--transition-slow) infinite; } -.chat-tool-call--error summary { +.chat-tool-call--error summary, +.chat-tool-call--error .chat-tool-call-summary { color: var(--color-error); } @@ -1782,20 +1764,14 @@ User messages in direct (model-loop) chat carry a compact edit affordance inline background: var(--color-error); } -.chat-tool-call-row--error { - background: color-mix(in srgb, var(--color-error) 10%, transparent); - border-radius: var(--radius-sm); - padding: var(--space-xs); -} - .chat-tool-calls--compact .chat-tool-calls-header, .chat-tool-calls--compact .chat-tool-calls-group-summary, .chat-tool-calls--compact .chat-tool-calls-names, .chat-tool-calls--compact .chat-tool-calls-group-status, .chat-tool-calls--compact .chat-tool-call summary, +.chat-tool-calls--compact .chat-tool-call-summary, .chat-tool-calls--compact .chat-tool-call-content, .chat-tool-calls--compact .chat-tool-call-preview, -.chat-tool-calls--compact .chat-tool-call-value, .chat-tool-calls-group--compact .chat-tool-calls-group-summary, .chat-tool-calls-group--compact .chat-tool-calls-names { font-size: 0.6875rem; @@ -2615,7 +2591,8 @@ Queued-message banners stack above the composer input with a capped scroll area, } .chat-tool-calls-group-summary, - .chat-tool-call summary { + .chat-tool-call summary, + .chat-tool-call-summary { flex-wrap: nowrap; flex-direction: row; align-items: center; diff --git a/packages/dashboard/app/components/StandardChatSurface.tsx b/packages/dashboard/app/components/StandardChatSurface.tsx index baf8e802d1..9051127abb 100644 --- a/packages/dashboard/app/components/StandardChatSurface.tsx +++ b/packages/dashboard/app/components/StandardChatSurface.tsx @@ -15,6 +15,7 @@ import { openNativeStructure } from "./nativeStructureNavigation"; import { nativeStructureChatRefMatcher, parseNativeStructureChatRef, splitNativeStructureChatRefMatch } from "./nativeStructureChatRef"; import { MicButton } from "./MicButton"; import { useComposerDictation } from "../hooks/useComposerDictation"; +import { ToolCallDetails, formatToolArgsPreview, formatToolPreview, hasToolCallDetails } from "./ToolCallDetails"; export interface StandardRoomContext { roomName: string; @@ -149,26 +150,8 @@ export function formatModelTag(provider?: string | null, modelId?: string | null return formatted.length > 30 ? `${formatted.slice(0, 30)}…` : formatted; } -function truncateToolValue(value: string, maxLength: number): string { - return value.length <= maxLength ? value : `${value.slice(0, maxLength)}…`; -} - -function formatToolArgsSummary(args?: Record): string | null { - if (!args) return null; - const entries = Object.entries(args); - if (entries.length === 0) return null; - return entries.map(([key, value]) => { - const stringValue = typeof value === "string" ? value : (() => { - try { return JSON.stringify(value); } catch { return String(value); } - })(); - return `${key}=${truncateToolValue(stringValue, 50)}`; - }).join(", "); -} - function formatToolResultSummary(result: unknown): string | null { - if (result === undefined) return null; - if (typeof result === "string") return truncateToolValue(result, 200); - try { return truncateToolValue(JSON.stringify(result), 200); } catch { return truncateToolValue(String(result), 200); } + return formatToolPreview(result, 200); } function buildFailureReferenceHref(reference: FailureInfo["reference"]): string | null { @@ -242,22 +225,33 @@ export function renderStandardToolCalls( } const isRunning = toolCall.status === "running"; const isError = toolCall.status === "completed" && toolCall.isError; - const argsSummary = formatToolArgsSummary(toolCall.args); + const argsSummary = formatToolArgsPreview(toolCall.args); const resultSummary = formatToolResultSummary(toolCall.result); const summaryPreview = isRunning ? argsSummary : resultSummary ? `${t("chat.toolCallResultPrefix", "result")}: ${resultSummary}` : argsSummary ? `${t("chat.toolCallArgsPrefix", "args")}: ${argsSummary}` : null; const statusLabel = isRunning ? t("chat.toolCallStatusRunning", "running") : isError ? t("chat.toolCallStatusError", "error") : t("chat.toolCallStatusCompleted", "completed"); + const className = `chat-tool-call${isRunning ? " chat-tool-call--running" : ""}${isError ? " chat-tool-call--error" : ""}`; + const summary = ( + <> +