From 77fe83d38e59a72a98a148f18f0bb139f7306eaa Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Tue, 17 Mar 2026 16:50:29 +0000 Subject: [PATCH] refactor(site/src/pages/AgentsPage): clean up RenderBlock types and dead fields RenderBlock's file-reference variant used camelCase field names (fileName, startLine, endLine) while the API sends snake_case. The parser destructured and renamed every field. The file variant was defined inline duplicating ChatFilePart. Replace both inline definitions with direct references to the generated ChatFilePart and ChatFileReferencePart types. The file-reference parser case becomes a pass-through. The file case now constructs a typed object instead of casting Record. Remove dead fields confirmed by Kyle: - title on thinking/reasoning blocks (never populated by backend) - text on file-reference blocks (abandoned file comment feature) Without title, ReasoningDisclosure's disclosure button path was dead code. Removed the button, its useState, and ChevronDownIcon. The component now renders inline text or a streaming placeholder. Cascading dead code: asOptionalTitle, appendParsedTextBlock wrapper, appendStreamTextBlock alias, createBlock helper (identical branches), joinText parameter (no production caller). Refs #23168 --- .../pages/AgentsPage/AgentDetail.stories.tsx | 19 +---- .../AgentDetail/ConversationTimeline.tsx | 75 ++++--------------- .../AgentsPage/AgentDetail/blockUtils.test.ts | 67 ++--------------- .../AgentsPage/AgentDetail/blockUtils.ts | 25 +------ .../AgentDetail/messageParsing.test.ts | 27 +++---- .../AgentsPage/AgentDetail/messageParsing.ts | 65 +++++----------- .../AgentDetail/streamState.test.ts | 49 ++++++++---- .../AgentsPage/AgentDetail/streamState.ts | 29 +++---- .../src/pages/AgentsPage/AgentDetail/types.ts | 17 +---- 9 files changed, 106 insertions(+), 267 deletions(-) diff --git a/site/src/pages/AgentsPage/AgentDetail.stories.tsx b/site/src/pages/AgentsPage/AgentDetail.stories.tsx index d9589fb36e10e..1147c9bfb6348 100644 --- a/site/src/pages/AgentsPage/AgentDetail.stories.tsx +++ b/site/src/pages/AgentsPage/AgentDetail.stories.tsx @@ -653,7 +653,7 @@ export const WithSubagentCards: Story = { }, }; -/** Reasoning part without title renders inline (no disclosure). */ +/** Completed reasoning part renders inline. */ export const WithReasoningInline: Story = { parameters: { queries: buildQueries( @@ -687,7 +687,7 @@ export const WithReasoningInline: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - // Reasoning text renders inline, not behind a disclosure. + // Reasoning text renders inline. expect(canvas.getByText("Reasoning body")).toBeInTheDocument(); expect(canvas.queryByRole("button", { name: "Thinking" })).toBeNull(); }, @@ -991,10 +991,9 @@ export const SidebarWithSingleRepo: Story = { }, }; /** - * Streaming reasoning part via WebSocket — renders collapsed and - * can be expanded on click. + * Streaming reasoning part via WebSocket — renders inline text. */ -export const StreamedReasoningCollapsed: Story = { +export const StreamedReasoning: Story = { parameters: { queries: buildQueries( { @@ -1015,7 +1014,6 @@ export const StreamedReasoningCollapsed: Story = { message_part: { part: { type: "reasoning", - title: "Plan migration", text: "Streaming reasoning body", }, }, @@ -1026,16 +1024,7 @@ export const StreamedReasoningCollapsed: Story = { }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const user = userEvent.setup(); - - const reasoningToggle = await canvas.findByRole("button", { - name: "Plan migration", - }); - expect(reasoningToggle).toHaveAttribute("aria-expanded", "false"); - - await user.click(reasoningToggle); - expect(reasoningToggle).toHaveAttribute("aria-expanded", "true"); await expect( canvas.findByText("Streaming reasoning body"), ).resolves.toBeInTheDocument(); diff --git a/site/src/pages/AgentsPage/AgentDetail/ConversationTimeline.tsx b/site/src/pages/AgentsPage/AgentDetail/ConversationTimeline.tsx index ee8b354ea6a27..8637cdf3048b9 100644 --- a/site/src/pages/AgentsPage/AgentDetail/ConversationTimeline.tsx +++ b/site/src/pages/AgentsPage/AgentDetail/ConversationTimeline.tsx @@ -17,7 +17,7 @@ import { TooltipContent, TooltipTrigger, } from "components/Tooltip/Tooltip"; -import { ChevronDownIcon, PencilIcon } from "lucide-react"; +import { PencilIcon } from "lucide-react"; import { type FC, Fragment, @@ -43,12 +43,10 @@ import type { const ReasoningDisclosure: FC<{ id: string; - title?: string; text: string; isStreaming?: boolean; urlTransform?: UrlTransform; -}> = ({ id, title, text, isStreaming = false, urlTransform }) => { - const [isOpen, setIsOpen] = useState(false); +}> = ({ id, text, isStreaming = false, urlTransform }) => { const { visibleText } = useSmoothStreamingText({ fullText: text, isStreaming, @@ -57,10 +55,8 @@ const ReasoningDisclosure: FC<{ }); const displayText = isStreaming ? visibleText : text; const hasText = displayText.trim().length > 0; - const label = title ?? "Thinking"; - const showStreamingPlaceholder = isStreaming && !hasText; - if (!title && hasText) { + if (hasText) { return (
); } - const labelContent = ( - - {showStreamingPlaceholder ? ( - Thinking... - ) : ( - label - )} - - ); return (
- {hasText ? ( - - ) : ( -
- {labelContent} -
- )} - {isOpen && hasText ? ( -
- - {displayText} - -
- ) : null} +
+ + {isStreaming ? Thinking... : "Thinking"} + +
); }; @@ -190,7 +151,6 @@ function renderBlockList({ - {block.fileName}: - {block.startLine === block.endLine - ? block.startLine - : `${block.startLine}\u2013${block.endLine}`} + {block.file_name}: + {block.start_line === block.end_line + ? block.start_line + : `${block.start_line}\u2013${block.end_line}`} - {block.text && ( - - {block.text} - - )}
); case "tool": { @@ -408,9 +363,9 @@ const ChatMessageItem = memo<{ ) : ( ), ) diff --git a/site/src/pages/AgentsPage/AgentDetail/blockUtils.test.ts b/site/src/pages/AgentsPage/AgentDetail/blockUtils.test.ts index 8347bb6316d26..16955b4b4557d 100644 --- a/site/src/pages/AgentsPage/AgentDetail/blockUtils.test.ts +++ b/site/src/pages/AgentsPage/AgentDetail/blockUtils.test.ts @@ -50,10 +50,8 @@ describe("appendTextBlock", () => { }); it("appends a new thinking block to an empty list", () => { - const result = appendTextBlock([], "thinking", "pondering", "Deep thought"); - expect(result).toEqual([ - { type: "thinking", text: "pondering", title: "Deep thought" }, - ]); + const result = appendTextBlock([], "thinking", "pondering"); + expect(result).toEqual([{ type: "thinking", text: "pondering" }]); }); it("merges consecutive response blocks", () => { @@ -63,29 +61,13 @@ describe("appendTextBlock", () => { expect(result[0]).toEqual({ type: "response", text: "aaabbb" }); }); - it("merges consecutive thinking blocks with compatible titles", () => { - const blocks: RenderBlock[] = [ - { type: "thinking", text: "part1", title: "Reasoning" }, - ]; - const result = appendTextBlock(blocks, "thinking", "part2", "Reasoning"); + it("merges consecutive thinking blocks", () => { + const blocks: RenderBlock[] = [{ type: "thinking", text: "part1" }]; + const result = appendTextBlock(blocks, "thinking", "part2"); expect(result).toHaveLength(1); expect(result[0]).toEqual({ type: "thinking", text: "part1part2", - title: "Reasoning", - }); - }); - - it("merges thinking blocks with different titles using the new title", () => { - const blocks: RenderBlock[] = [ - { type: "thinking", text: "part1", title: "Analyzing" }, - ]; - const result = appendTextBlock(blocks, "thinking", "part2", "Planning"); - expect(result).toHaveLength(1); - expect(result[0]).toEqual({ - type: "thinking", - text: "part1part2", - title: "Planning", }); }); @@ -96,7 +78,6 @@ describe("appendTextBlock", () => { expect(result[1]).toEqual({ type: "thinking", text: "hmm", - title: undefined, }); }); @@ -107,20 +88,6 @@ describe("appendTextBlock", () => { expect(result[1]).toEqual({ type: "response", text: "after tool" }); }); - it("uses the custom joinText function when merging", () => { - const blocks: RenderBlock[] = [{ type: "response", text: "line1" }]; - const join = (a: string, b: string) => `${a}\n${b}`; - const result = appendTextBlock( - blocks, - "response", - "line2", - undefined, - join, - ); - expect(result).toHaveLength(1); - expect(result[0]).toEqual({ type: "response", text: "line1\nline2" }); - }); - it("does not mutate the original blocks array", () => { const blocks: RenderBlock[] = [{ type: "response", text: "original" }]; const result = appendTextBlock(blocks, "response", " added"); @@ -128,28 +95,4 @@ describe("appendTextBlock", () => { expect((blocks[0] as { text: string }).text).toBe("original"); expect(result).not.toBe(blocks); }); - - it("merges thinking block and uses new title", () => { - const blocks: RenderBlock[] = [ - { type: "thinking", text: "a", title: "Think" }, - ]; - const result = appendTextBlock(blocks, "thinking", "b", "Thinking deeply"); - expect(result).toHaveLength(1); - expect(result[0]).toEqual({ - type: "thinking", - text: "ab", - title: "Thinking deeply", - }); - }); - - it("merges thinking blocks when both have no title", () => { - const blocks: RenderBlock[] = [{ type: "thinking", text: "a" }]; - const result = appendTextBlock(blocks, "thinking", "b"); - expect(result).toHaveLength(1); - expect(result[0]).toEqual({ - type: "thinking", - text: "ab", - title: undefined, - }); - }); }); diff --git a/site/src/pages/AgentsPage/AgentDetail/blockUtils.ts b/site/src/pages/AgentsPage/AgentDetail/blockUtils.ts index 2ae1e56f0276e..a66b26e454b02 100644 --- a/site/src/pages/AgentsPage/AgentDetail/blockUtils.ts +++ b/site/src/pages/AgentsPage/AgentDetail/blockUtils.ts @@ -1,13 +1,6 @@ import { asString } from "components/ai-elements/runtimeTypeUtils"; import type { RenderBlock } from "./types"; -const createBlock = ( - type: "response" | "thinking", - text: string, - title?: string, -): RenderBlock => - type === "thinking" ? { type, text, title } : { type, text }; - export const asNonEmptyString = (value: unknown): string | undefined => { const next = asString(value).trim(); return next.length > 0 ? next : undefined; @@ -16,18 +9,11 @@ export const asNonEmptyString = (value: unknown): string | undefined => { /** * Append a text or thinking block to a render block list, merging * with the previous block when the types match. - * - * @param joinText Controls how existing and new text are concatenated - * when merging into an existing block. Callers that process - * complete message blocks typically join with a newline, while - * streaming callers concatenate directly. */ export const appendTextBlock = ( blocks: RenderBlock[], type: "response" | "thinking", text: string, - title?: string, - joinText: (current: string, next: string) => string = (a, b) => `${a}${b}`, ): RenderBlock[] => { if (!text.trim()) { return blocks; @@ -35,15 +21,12 @@ export const appendTextBlock = ( const nextBlocks = [...blocks]; const last = nextBlocks[nextBlocks.length - 1]; if (last && last.type === type) { - nextBlocks[nextBlocks.length - 1] = createBlock( + nextBlocks[nextBlocks.length - 1] = { type, - joinText(last.text, text), - type === "thinking" && last.type === "thinking" - ? (title ?? last.title) - : undefined, - ); + text: `${last.text}${text}`, + }; return nextBlocks; } - nextBlocks.push(createBlock(type, text, title)); + nextBlocks.push({ type, text }); return nextBlocks; }; diff --git a/site/src/pages/AgentsPage/AgentDetail/messageParsing.test.ts b/site/src/pages/AgentsPage/AgentDetail/messageParsing.test.ts index cfdf46483cd29..0adebe26ac6ad 100644 --- a/site/src/pages/AgentsPage/AgentDetail/messageParsing.test.ts +++ b/site/src/pages/AgentsPage/AgentDetail/messageParsing.test.ts @@ -115,11 +115,11 @@ describe("parseMessageContent", () => { it("parses a reasoning block", () => { const result = parseMessageContent([ - { type: "reasoning", text: "Let me think...", title: "Reasoning" }, + { type: "reasoning", text: "Let me think..." }, ]); expect(result.reasoning).toBe("Let me think..."); expect(result.blocks).toEqual([ - { type: "thinking", text: "Let me think...", title: "Reasoning" }, + { type: "thinking", text: "Let me think..." }, ]); }); @@ -263,17 +263,15 @@ describe("parseMessageContent", () => { start_line: 10, end_line: 15, content: "some added code lines", - text: "Consider using a constant here.", }, ]); expect(result.blocks).toHaveLength(1); expect(result.blocks[0]).toEqual({ type: "file-reference", - fileName: "src/main.go", - startLine: 10, - endLine: 15, + file_name: "src/main.go", + start_line: 10, + end_line: 15, content: "some added code lines", - text: "Consider using a constant here.", }); }); @@ -283,12 +281,11 @@ describe("parseMessageContent", () => { type: "file-reference", file_name: "bare.ts", content: "bare content", - text: "No line info.", }, ]); - const ref = result.blocks[0] as { startLine: number; endLine: number }; - expect(ref.startLine).toBe(0); - expect(ref.endLine).toBe(0); + const ref = result.blocks[0] as { start_line: number; end_line: number }; + expect(ref.start_line).toBe(0); + expect(ref.end_line).toBe(0); }); it("does not affect markdown when file-reference blocks are present", () => { @@ -300,7 +297,6 @@ describe("parseMessageContent", () => { start_line: 1, end_line: 2, content: "nit code content", - text: "Nit.", }, ]); expect(result.markdown).toBe("Hello"); @@ -308,11 +304,10 @@ describe("parseMessageContent", () => { expect(result.blocks[0]).toEqual({ type: "response", text: "Hello" }); expect(result.blocks[1]).toEqual({ type: "file-reference", - fileName: "a.go", - startLine: 1, - endLine: 2, + file_name: "a.go", + start_line: 1, + end_line: 2, content: "nit code content", - text: "Nit.", }); }); diff --git a/site/src/pages/AgentsPage/AgentDetail/messageParsing.ts b/site/src/pages/AgentsPage/AgentDetail/messageParsing.ts index 0ea8e00df92af..c65bd5cb3190e 100644 --- a/site/src/pages/AgentsPage/AgentDetail/messageParsing.ts +++ b/site/src/pages/AgentsPage/AgentDetail/messageParsing.ts @@ -1,6 +1,6 @@ import type * as TypesGen from "api/typesGenerated"; import { asRecord, asString } from "components/ai-elements/runtimeTypeUtils"; -import { appendTextBlock, asNonEmptyString } from "./blockUtils"; +import { appendTextBlock } from "./blockUtils"; import type { MergedTool, ParsedMessageContent, @@ -18,9 +18,6 @@ const appendText = (current: string, next: string): string => { return `${current}${next}`; }; -export const asOptionalTitle = (value: unknown): string | undefined => - asNonEmptyString(value); - const isSubagentToolName = (name: string): boolean => name === "spawn_agent" || name === "wait_agent" || name === "message_agent"; @@ -72,15 +69,6 @@ const emptyParsedMessageContent = (): ParsedMessageContent => ({ sources: [], }); -/** Wraps appendTextBlock using the same direct concatenation as - * the streaming path so both produce identical markdown. */ -const appendParsedTextBlock = ( - blocks: RenderBlock[], - type: "response" | "thinking", - text: string, - title?: string, -): RenderBlock[] => appendTextBlock(blocks, type, text, title); - export const ensureToolBlock = ( blocks: RenderBlock[], id: string, @@ -140,7 +128,7 @@ export const parseMessageContent = (content: unknown): ParsedMessageContent => { for (const [index, block] of content.entries()) { if (typeof block === "string") { parsed.markdown = appendText(parsed.markdown, block); - parsed.blocks = appendParsedTextBlock(parsed.blocks, "response", block); + parsed.blocks = appendTextBlock(parsed.blocks, "response", block); continue; } @@ -153,23 +141,13 @@ export const parseMessageContent = (content: unknown): ParsedMessageContent => { case "text": { const text = asString(typedBlock.text); parsed.markdown = appendText(parsed.markdown, text); - parsed.blocks = appendParsedTextBlock( - parsed.blocks, - "response", - text, - ); + parsed.blocks = appendTextBlock(parsed.blocks, "response", text); break; } case "reasoning": { const text = asString(typedBlock.text); - const title = asOptionalTitle(typedBlock.title); parsed.reasoning = appendText(parsed.reasoning, text); - parsed.blocks = appendParsedTextBlock( - parsed.blocks, - "thinking", - text, - title, - ); + parsed.blocks = appendTextBlock(parsed.blocks, "thinking", text); break; } case "tool-call": { @@ -191,18 +169,16 @@ export const parseMessageContent = (content: unknown): ParsedMessageContent => { break; } case "file-reference": { - const text = asString(typedBlock.text); const fileName = asString(typedBlock.file_name); const startLine = Number(typedBlock.start_line) || 0; const endLine = Number(typedBlock.end_line) || startLine; - const contentStr = asString(typedBlock.content); + const content = asString(typedBlock.content); parsed.blocks.push({ type: "file-reference", - fileName, - startLine, - endLine, - content: contentStr, - text, + file_name: fileName, + start_line: startLine, + end_line: endLine, + content, }); break; } @@ -224,17 +200,18 @@ export const parseMessageContent = (content: unknown): ParsedMessageContent => { parsed.blocks = ensureToolBlock(parsed.blocks, id); break; } - case "file": - if ( - typedBlock.media_type && - (typedBlock.data || typedBlock.file_id) - ) { + case "file": { + const mediaType = asString(typedBlock.media_type); + const data = asString(typedBlock.data) || undefined; + const fileId = asString(typedBlock.file_id) || undefined; + if (mediaType && (data || fileId)) { parsed.blocks = [ ...parsed.blocks, - typedBlock as Extract, + { type: "file", media_type: mediaType, data, file_id: fileId }, ]; } break; + } case "source": { const url = asString(typedBlock.url); const title = asString(typedBlock.title); @@ -265,11 +242,7 @@ export const parseMessageContent = (content: unknown): ParsedMessageContent => { default: { const text = asString(typedBlock.text); parsed.markdown = appendText(parsed.markdown, text); - parsed.blocks = appendParsedTextBlock( - parsed.blocks, - "response", - text, - ); + parsed.blocks = appendTextBlock(parsed.blocks, "response", text); break; } } @@ -287,7 +260,7 @@ export const parseMessageContent = (content: unknown): ParsedMessageContent => { return { ...emptyParsedMessageContent(), markdown, - blocks: appendParsedTextBlock([], "response", markdown), + blocks: appendTextBlock([], "response", markdown), }; } @@ -300,7 +273,7 @@ export const parseMessageContent = (content: unknown): ParsedMessageContent => { return { ...emptyParsedMessageContent(), markdown, - blocks: appendParsedTextBlock([], "response", markdown), + blocks: appendTextBlock([], "response", markdown), }; }; diff --git a/site/src/pages/AgentsPage/AgentDetail/streamState.test.ts b/site/src/pages/AgentsPage/AgentDetail/streamState.test.ts index 387948d3245ef..2f3230240b1b0 100644 --- a/site/src/pages/AgentsPage/AgentDetail/streamState.test.ts +++ b/site/src/pages/AgentsPage/AgentDetail/streamState.test.ts @@ -56,15 +56,14 @@ describe("applyMessagePartToStreamState", () => { const result = applyMessagePartToStreamState(null, { type: "reasoning", text: "Let me reason...", - title: "Analysis", }); expect(result).not.toBeNull(); expect(result!.blocks).toEqual([ - { type: "thinking", text: "Let me reason...", title: "Analysis" }, + { type: "thinking", text: "Let me reason..." }, ]); }); - it("returns prev for reasoning part with no text and no title", () => { + it("returns prev for reasoning part with empty text", () => { const prev = createEmptyStreamState(); const result = applyMessagePartToStreamState(prev, { type: "reasoning", @@ -73,16 +72,6 @@ describe("applyMessagePartToStreamState", () => { expect(result).toBe(prev); }); - it("returns prev for reasoning part with only title and no text", () => { - const prev = createEmptyStreamState(); - const result = applyMessagePartToStreamState(prev, { - type: "reasoning", - text: "", - title: "Some Title", - }); - expect(result).toBe(prev); - }); - it("creates tool call entry from tool-call part", () => { const result = applyMessagePartToStreamState(null, { type: "tool-call", @@ -330,6 +319,40 @@ describe("applyMessagePartToStreamState", () => { expect(state).toBe(afterFirst); expect(state!.sources).toHaveLength(1); }); + + it("produces correct tool-result shape with is_error through buildStreamTools", () => { + let state: StreamState | null = null; + state = applyMessagePartToStreamState(state, { + type: "tool-call", + tool_name: "bash", + tool_call_id: "tc-1", + args: { command: "rm -rf /" }, + }); + state = applyMessagePartToStreamState(state, { + type: "tool-result", + tool_name: "bash", + tool_call_id: "tc-1", + result: { error: "permission denied" }, + is_error: true, + }); + expect(state).not.toBeNull(); + expect(state!.toolResults["tc-1"]).toMatchObject({ + id: "tc-1", + name: "bash", + result: { error: "permission denied" }, + isError: true, + }); + const tools = buildStreamTools(state); + expect(tools).toHaveLength(1); + expect(tools[0]).toEqual({ + id: "tc-1", + name: "bash", + args: { command: "rm -rf /" }, + result: { error: "permission denied" }, + isError: true, + status: "error", + }); + }); }); describe("buildStreamTools", () => { diff --git a/site/src/pages/AgentsPage/AgentDetail/streamState.ts b/site/src/pages/AgentsPage/AgentDetail/streamState.ts index 98f822d6b4622..3e04f20b0de04 100644 --- a/site/src/pages/AgentsPage/AgentDetail/streamState.ts +++ b/site/src/pages/AgentsPage/AgentDetail/streamState.ts @@ -1,10 +1,6 @@ import { asString } from "components/ai-elements/runtimeTypeUtils"; import { appendTextBlock } from "./blockUtils"; -import { - asOptionalTitle, - ensureToolBlock, - parseToolResultIsError, -} from "./messageParsing"; +import { ensureToolBlock, parseToolResultIsError } from "./messageParsing"; import { mergeStreamPayload } from "./streamingJson"; import type { MergedTool, RenderBlock, StreamState } from "./types"; @@ -17,9 +13,6 @@ export const createEmptyStreamState = (): StreamState => ({ sources: [], }); -/** Streaming variant — uses direct concatenation (the default joinText). */ -const appendStreamTextBlock = appendTextBlock; - export const applyMessagePartToStreamState = ( prev: StreamState | null, part: Record, @@ -35,7 +28,7 @@ export const applyMessagePartToStreamState = ( } return { ...nextState, - blocks: appendStreamTextBlock(nextState.blocks, "response", text), + blocks: appendTextBlock(nextState.blocks, "response", text), }; } case "reasoning": { @@ -43,15 +36,9 @@ export const applyMessagePartToStreamState = ( if (!text) { return prev; } - const title = asOptionalTitle(part.title); return { ...nextState, - blocks: appendStreamTextBlock( - nextState.blocks, - "thinking", - text, - title, - ), + blocks: appendTextBlock(nextState.blocks, "thinking", text), }; } case "tool-call": { @@ -137,17 +124,21 @@ export const applyMessagePartToStreamState = ( }, }; } - case "file": - if (!part.media_type || (!part.data && !part.file_id)) { + case "file": { + const mediaType = asString(part.media_type); + const data = asString(part.data) || undefined; + const fileId = asString(part.file_id) || undefined; + if (!mediaType || (!data && !fileId)) { return prev; } return { ...nextState, blocks: [ ...nextState.blocks, - part as Extract, + { type: "file", media_type: mediaType, data, file_id: fileId }, ], }; + } case "source": { const url = asString(part.url); const title = asString(part.title); diff --git a/site/src/pages/AgentsPage/AgentDetail/types.ts b/site/src/pages/AgentsPage/AgentDetail/types.ts index f8acfa1243972..aeae99bc7fa16 100644 --- a/site/src/pages/AgentsPage/AgentDetail/types.ts +++ b/site/src/pages/AgentsPage/AgentDetail/types.ts @@ -30,26 +30,13 @@ export type RenderBlock = | { type: "thinking"; text: string; - title?: string; } | { type: "tool"; id: string; } - | { - type: "file"; - media_type: string; - data?: string; // base64, absent when file_id is available - file_id?: string; - } - | { - type: "file-reference"; - fileName: string; - startLine: number; - endLine: number; - content: string; - text: string; - } + | TypesGen.ChatFilePart + | TypesGen.ChatFileReferencePart | { type: "sources"; sources: Array<{ url: string; title: string }>;