@@ -6,20 +6,20 @@ import { memo } from "react";
66import { Button , LinkButton } from "~/components/primitives/Buttons" ;
77import { Callout } from "~/components/primitives/Callout" ;
88import { renderPart , toSafeUrl } from "~/components/runs/v3/agent/AgentMessageView" ;
9- import { ToolUseRow } from "~/components/runs/v3/ai/AIChatMessages" ;
109import { sameOriginPath } from "./navigate-target" ;
1110import { hasToolProgressLine , IN_FLIGHT_TOOL_STATES } from "./progress-line" ;
1211import { useTranscriptAutoScroll } from "./useTranscriptAutoScroll" ;
1312import {
1413 ChatActionsRow ,
1514 ChatCardSlot ,
15+ ChatPendingTool ,
1616 ChatProgress ,
1717 ChatText ,
18- ChatToolRow ,
1918 ChatTranscript ,
2019 ChatTurn ,
2120 ChatWakeSlot ,
2221} from "./chat-layout" ;
22+ import { toolPendingLabel } from "./tool-labels" ;
2323import { reportBlockFromToolPart } from "./report-block-adapter" ;
2424import type { ResolvedUri } from "./ReportView" ;
2525import { ViewBlocks } from "./view-catalog" ;
@@ -85,8 +85,9 @@ function viewSpecFor(part: UIMessage["parts"][number]): { blocks: unknown[] } |
8585 * it retyped.
8686 *
8787 * Both replace the generic tool row: a rendered card already says everything the
88- * raw JSON would. A `get_report` part that can't be adapted (still streaming, or
89- * an error) returns null and keeps its tool row, so the failure stays visible.
88+ * raw JSON would. A `get_report` part that can't be adapted returns null and
89+ * falls through — to the pending pill while it is still streaming, to the tool row
90+ * once it has failed, so the failure stays visible.
9091 */
9192function blocksFor ( part : UIMessage [ "parts" ] [ number ] ) : unknown [ ] | null {
9293 const spec = viewSpecFor ( part ) ;
@@ -105,9 +106,12 @@ function blocksFor(part: UIMessage["parts"][number]): unknown[] | null {
105106 * Everything the panel styles itself is handled here; the rest falls through to
106107 * the shared `renderPart` so agent output still looks the same across the app.
107108 * The differences: text is always the rendered markdown (no raw toggle) at the
108- * dashboard's default size, and an in-flight tool row is static with the spinner
109- * on a separate line. Citations are handled a level up, where a run of them can
110- * be grouped into one row.
109+ * dashboard's default size, and an in-flight tool call is a pending pill rather
110+ * than a tool row streaming its input JSON — the input is the agent's business,
111+ * and watching it arrive character by character only to have it replaced by a
112+ * card is noise. Once the call lands, the part renders exactly as it always did.
113+ * Citations are handled a level up, where a run of them can be grouped into one
114+ * row.
111115 */
112116function renderDashboardPart ( part : UIMessage [ "parts" ] [ number ] , i : number ) {
113117 const p = part as {
@@ -116,8 +120,6 @@ function renderDashboardPart(part: UIMessage["parts"][number], i: number) {
116120 url ?: string ;
117121 title ?: string ;
118122 state ?: string ;
119- input ?: unknown ;
120- toolCallId ?: string ;
121123 } ;
122124 const type = part . type as string ;
123125
@@ -126,19 +128,7 @@ function renderDashboardPart(part: UIMessage["parts"][number], i: number) {
126128 }
127129
128130 if ( type . startsWith ( "tool-" ) && IN_FLIGHT_TOOL_STATES . has ( p . state ?? "" ) ) {
129- const toolName = type . slice ( 5 ) ;
130- return (
131- < ChatToolRow key = { i } >
132- < ToolUseRow
133- tool = { {
134- toolCallId : p . toolCallId ?? `tool-${ i } ` ,
135- toolName,
136- inputJson : JSON . stringify ( p . input ?? { } , null , 2 ) ,
137- } }
138- />
139- < ChatProgress > Running { toolName } …</ ChatProgress >
140- </ ChatToolRow >
141- ) ;
131+ return < ChatPendingTool key = { i } label = { `${ toolPendingLabel ( type . slice ( 5 ) ) } …` } /> ;
142132 }
143133
144134 return renderPart ( part , i ) ;
0 commit comments